├── .gitignore
├── .travis.yml
├── Makefile
├── README.md
├── apple.c
├── apple.h
├── base.c
├── base.h
├── base64.c
├── base64.h
├── debian
├── changelog
├── compat
├── control
├── copyright
├── init.d
├── postinst
├── postrm
├── redsocks.8
├── redsocks.conf
├── redsocks.default
├── redsocks.docs
├── redsocks.install
├── redsocks.manpages
├── rules
├── source
│ └── format
└── watch
├── debug.c
├── dnstc.c
├── dnstc.h
├── dnsu2t.c
├── dnsu2t.h
├── doc
├── balabit-TPROXY-README.txt
├── iptables-packet-flow-ng.png
├── iptables-packet-flow.png
├── rfc1928-socks5.txt
├── rfc1929-socks5-auth.txt
├── rfc1961-socks5-gssapi.txt
├── rfc2617-http-authentication.txt
├── rfc2817-http-connect.txt
├── rfc3089-socks-ipv6.txt
├── socks4.protocol.txt
└── socks4a.protocol.txt
├── http-auth.c
├── http-auth.h
├── http-connect.c
├── http-relay.c
├── libc-compat.h
├── libevent-compat.h
├── list.h
├── log.c
├── log.h
├── main.c
├── main.h
├── md5.c
├── md5.h
├── parser.c
├── parser.h
├── redsocks.c
├── redsocks.conf.example
├── redsocks.h
├── redsocks.service
├── redudp.c
├── redudp.h
├── socks4.c
├── socks5.c
├── socks5.h
├── tdestroy.c
├── tests
├── build
├── cleanup
├── conftest.py
├── dante
│ ├── Dockerfile
│ ├── danted-1080.conf
│ ├── danted-1081.conf
│ └── danted-waitif
├── inetd
│ ├── Dockerfile
│ └── testing
├── login
├── prlimit-nofile.c
├── regw
│ ├── Dockerfile
│ └── redsocks.conf
├── run
├── squid
│ ├── Dockerfile
│ ├── basic.passwd
│ ├── digest.passwd
│ ├── squid-8.conf
│ └── squid-9.conf
├── tank
│ ├── Dockerfile
│ └── benchmark.py
├── test_smoke.py
└── web
│ └── Dockerfile
├── timer.h
├── tools
└── git-repack.sh
├── tsearch.c
├── tsearch.h
├── utils.c
├── utils.h
└── version.h
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | config.h
3 | tags
4 | redsocks
5 | .depend
6 | /gen
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | - osx
4 |
5 | addons:
6 | apt:
7 | packages:
8 | - libevent-dev
9 |
10 | language: c
11 |
12 | compiler:
13 | - gcc
14 | - clang
15 |
16 | script:
17 | - make all
18 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | -include make.conf
2 | OBJS := parser.o main.o redsocks.o apple.o tdestroy.o tsearch.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o gen/version.o
3 | ifeq ($(DBG_BUILD),1)
4 | OBJS += debug.o
5 | endif
6 | SRCS := $(OBJS:.o=.c)
7 | CONF := config.h
8 | DEPS := .depend
9 | OUT := redsocks
10 | VERSION := 0.5
11 |
12 | LIBS := -levent_core
13 | ifeq ($(DBG_BUILD),1)
14 | # -levent_extra is required only for `http` and `debug`
15 | LIBS += -levent_extra
16 | endif
17 | CFLAGS += -g -O2
18 | # _GNU_SOURCE is used to get splice(2), it also implies _BSD_SOURCE
19 | override CFLAGS += -std=c99 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall
20 | #override CFLAGS += -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall
21 |
22 | all: $(OUT)
23 |
24 | .PHONY: all clean distclean test
25 |
26 | tags: *.c *.h
27 | ctags -R
28 |
29 | $(CONF):
30 | @case `uname` in \
31 | Linux*) \
32 | echo "#define USE_IPTABLES" >$(CONF) \
33 | ;; \
34 | OpenBSD) \
35 | echo "#define USE_PF" >$(CONF) \
36 | ;; \
37 | *) \
38 | echo "Unknown system, only generic firewall code is compiled" 1>&2; \
39 | echo "/* Unknown system, only generic firewall code is compiled */" >$(CONF) \
40 | ;; \
41 | esac
42 | ifeq ($(DBG_BUILD),1)
43 | echo "#define DBG_BUILD 1" >>$(CONF)
44 | endif
45 |
46 | # Dependency on .git is useful to rebuild `version.c' after commit, but it breaks non-git builds.
47 | gen/version.c: *.c *.h gen/.build
48 | rm -f $@.tmp
49 | echo '/* this file is auto-generated during build */' > $@.tmp
50 | echo '#include "../version.h"' >> $@.tmp
51 | echo 'const char* redsocks_version = ' >> $@.tmp
52 | if [ -d .git ]; then \
53 | echo '"redsocks.git/'`git describe --tags`'"'; \
54 | if [ `git status --porcelain | grep -v -c '^??'` != 0 ]; then \
55 | echo '"'"-unclean-$$(date --rfc-3339=seconds | tr ' ' 'T')-$${USER}@$$(uname -n)"'"'; \
56 | fi \
57 | else \
58 | echo '"redsocks/$(VERSION)"'; \
59 | fi >> $@.tmp
60 | echo ';' >> $@.tmp
61 | mv -f $@.tmp $@
62 |
63 | gen/.build:
64 | mkdir -p gen
65 | touch $@
66 |
67 | base.c: $(CONF)
68 |
69 | $(DEPS): $(SRCS)
70 | gcc -MM $(SRCS) 2>/dev/null >$(DEPS) || \
71 | ( \
72 | for I in $(wildcard *.h); do \
73 | echo $(I)
74 | export $${I//[-.]/_}_DEPS="`sed '/^\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$I`"; \
75 | done; \
76 | echo -n >$(DEPS); \
77 | for SRC in $(SRCS); do \
78 | echo -n "$${SRC%.c}.o: " >>$(DEPS); \
79 | export SRC_DEPS="`sed '/\#[ \t]*include \?"\(.*\)".*/!d;s//\1/' $$SRC | sort`"; \
80 | while true; do \
81 | export SRC_DEPS_OLD="$$SRC_DEPS"; \
82 | export SRC_DEEP_DEPS=""; \
83 | for HDR in $$SRC_DEPS; do \
84 | eval export SRC_DEEP_DEPS="\"$$SRC_DEEP_DEPS \$$$${HDR//[-.]/_}_DEPS\""; \
85 | done; \
86 | export SRC_DEPS="`echo $$SRC_DEPS $$SRC_DEEP_DEPS | sed 's/ */\n/g' | sort -u`"; \
87 | test "$$SRC_DEPS" = "$$SRC_DEPS_OLD" && break; \
88 | done; \
89 | echo $$SRC $$SRC_DEPS >>$(DEPS); \
90 | done; \
91 | )
92 |
93 | -include $(DEPS)
94 |
95 | $(OUT): $(OBJS)
96 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
97 |
98 | clean:
99 | $(RM) $(OUT) $(CONF) $(OBJS)
100 |
101 | distclean: clean
102 | $(RM) tags $(DEPS)
103 | $(RM) -r gen
104 |
105 | tests/__build-tstamp__: $(OUT) tests/[a-z]* tests/[a-z]*/*
106 | cd tests && ./build
107 | touch $@
108 |
109 | tests/prlimit-nofile: tests/prlimit-nofile.c
110 | $(CC) $(CFLAGS) -o $@ $^
111 |
112 | test: tests/__build-tstamp__ tests/prlimit-nofile
113 | cd tests && env $(TEST_ENV) ./run
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redsocks – transparent TCP-to-proxy redirector
2 |
3 | This tool allows you to redirect any TCP connection to SOCKS or HTTPS
4 | proxy using your firewall, so redirection may be system-wide or network-wide.
5 |
6 | When is redsocks useful?
7 |
8 | * you want to route part of TCP traffic via OpenSSH `DynamicForward` Socks5
9 | port using firewall policies. That was original redsocks development goal;
10 | * you use DVB ISP and this ISP provides internet connectivity with some
11 | special daemon that may be also called "Internet accelerator" and the
12 | accelerator acts as a proxy and has no "transparent proxy" feature and you
13 | need it. [Globax](http://www.globax.biz) was an example of alike accelerator,
14 | but Globax 5 has transparent proxy feature. That was the second redsocks`
15 | development goal;
16 | * you have to pass traffic through proxy due to corporate network limitation.
17 | That was never a goal for redsocks, but users have reported success with
18 | some proxy configurations.
19 |
20 | When is redsocks probably a wrong tool?
21 |
22 | * redirecting traffic to [tor](https://www.torproject.org). First, you **have**
23 | to [use tor-aware software for anonymity](https://www.torproject.org/download/download.html.en#warning).
24 | Second, [use `TransPort`](https://trac.torproject.org/projects/tor/wiki/doc/TransparentProxy)
25 | if you don't actually need anonymity. Third, question everything :-)
26 | * trying to redirect traffic of significant number of connections over single
27 | SSH connection. That's not exactly [TCP over TCP](http://sites.inka.de/bigred/devel/tcp-tcp.html),
28 | but [head-of-line blocking](https://en.wikipedia.org/wiki/Head-of-line_blocking)
29 | will still happen and performance of real-time applications (IM, interactive
30 | Web applications) may be degraded during bulk transfers;
31 | * trying to make non-transparent HTTP-proxy (not HTTPS-proxy) transparent using
32 | `http-relay` module. First, it will likely be broken as the code is hack.
33 | Second, the code is vulnerable to `CVE-2009-0801` and will unlikely be ever fixed;
34 | * making "really" transparent proxy, redsocks acts at TCP level, so three-way
35 | handshake is completed and redsocks accepts connection before connection
36 | through proxy (and _to_ proxy) is established;
37 | * trying to redirect traffic of significant number of connections in
38 | resource-constrained environment like SOHO Linux router. Throughput of single
39 | connection may be good enough like 40 Mbit/s
40 | on [TP-Link TD-W8980](https://wiki.openwrt.org/toh/tp-link/td-w8980),
41 | but amount of concurrent connections may be limiting factor as TCP buffers
42 | are still consumed;
43 | * redirecting traffic to proxy on mobile device running Android or iOS as it'll require
44 | [rooting](https://en.wikipedia.org/wiki/Rooting_(Android)) to update firewall
45 | rules. Probably, the better way is to use on-device VPN daemon to intercept
46 | traffic via [`VpnService` API for Android](https://developer.android.com/reference/android/net/VpnService.html)
47 | and [`NETunnelProvider` family of APIs for iOS](https://developer.apple.com/documentation/networkextension).
48 | That may require some code doing [TCP Reassembly](https://wiki.wireshark.org/TCP_Reassembly)
49 | like [`tun2socks`](https://github.com/ambrop72/badvpn/wiki/Tun2socks).
50 |
51 | Linux/iptables is supported. OpenBSD/pf and FreeBSD/ipfw may work with some
52 | hacks. The author has no permanent root access to machines running OpenBSD,
53 | FreeBSD and MacOSX to test and develop for these platforms.
54 |
55 | [Transocks](http://transocks.sourceforge.net/) is alike project but it has
56 | noticeable performance penality.
57 |
58 | [Transsocks_ev](http://oss.tiggerswelt.net/transocks_ev/)
59 | is alike project too, but it has no HTTPS-proxy support
60 | and does not support authentication.
61 |
62 | Several Android apps also use redsocks under-the-hood:
63 | [ProxyDroid](https://github.com/madeye/proxydroid)
64 | [](https://market.android.com/details?id=org.proxydroid) and
65 | [sshtunnel](https://code.google.com/archive/p/sshtunnel/)
66 | [](https://market.android.com/details?id=org.sshtunnel).
67 | And that's over 1'500'000 downloads! Wow!
68 |
69 | ## Features
70 |
71 | Redirect any TCP connection to Socks4, Socks5 or HTTPS (HTTP/CONNECT)
72 | proxy server.
73 |
74 | Login/password authentication is supported for Socks5/HTTPS connections.
75 | Socks4 supports only username, password is ignored. for HTTPS, currently
76 | only Basic and Digest scheme is supported.
77 |
78 | Redirect UDP packets via Socks5 proxy server. NB: UDP still goes via UDP, so
79 | you can't relay UDP via OpenSSH.
80 |
81 | Handle DNS/UDP queries sending "truncated reply" as an answer or making them
82 | DNS/TCP queries to some recursive resolver.
83 |
84 | Redirect any HTTP connection to proxy that does not support transparent
85 | proxying (e.g. old SQUID had broken `acl myport' for such connections).
86 |
87 | ### Enforcing DNS over TCP using `dnstc`
88 |
89 | DNS is running over UDP and it may be an issue in some environments as proxy
90 | servers usually don't handle UDP as a first-class citizen. Redsocks includes
91 | `dnstc` that is fake and really dumb DNS server that returns "truncated answer"
92 | to every query via UDP. RFC-compliant resolver should repeat same query via TCP
93 | in this case - so the request can be redirected using usual redsocks facilities.
94 |
95 | Known compliant resolvers are:
96 |
97 | * bind9 (server);
98 | * dig, nslookup (tools based on bind9 code).
99 |
100 | Known non-compliant resolvers are:
101 |
102 | * eglibc resolver fails without any attempt to send request via TCP;
103 | * powerdns-recursor can't properly startup without UDP connectivity as it
104 | can't load root hints.
105 |
106 | On the other hand, DNS via TCP using bind9 may be painfully slow.
107 | If your bind9 setup is really slow, you may want to try
108 | [pdnsd](http://www.phys.uu.nl/~rombouts/pdnsd.html) caching server
109 | that can run in TCP-only mode.
110 |
111 | ### Relaying DNS/UDP to DNS/TCP via `dnsu2t`
112 |
113 | The code acts as DNS server that multiplexes several UDP queries into single
114 | stream of TCP queries over keep-alive connection to upstream DNS server that
115 | should be recursive resolver. TCP connection may be handled by `redsocks`
116 | itself if firewall is configured with corresponding rules.
117 |
118 | Different resolvers have different timeouts and allow different count of
119 | in-flight connections, so you have to tune options yourself for optimal
120 | performance (with some black magic, as script testing for optimal DNS/TCP
121 | connection parameters is not written yet).
122 |
123 | There are other programs doing alike job (with, probably, different bugs)
124 |
125 | * [ttdnsd](http://www.mulliner.org/collin/ttdnsd.php)
126 | * [dns2socks](https://github.com/qiuzi/dns2socks) for Windows
127 | * [tcpdnsproxy](https://github.com/jtripper/dns-tcp-socks-proxy)
128 |
129 | ## Source
130 |
131 | Source is available at [ GitHub](https://github.com/darkk/redsocks).
132 |
133 | Issue tracker is also at GitHub, but keep in mind that the project is not
134 | actively maintained, so feature requests will unlikely be implemented within
135 | reasonable timeframe. Reproducable bugs having clean desciption will likely be
136 | fixed. Destiny of hard-to-reproduce bugs is hard to predict.
137 |
138 | New network protocols will unlikely be implemented within this source tree, but
139 | if you're seeking for censorship circumvention protocols, you may want to take
140 | a look at [redsocks2](https://github.com/semigodking/redsocks) by Zhuofei Wang
141 | AKA @semigodking who is actively maintaining the fork with GFW in mind.
142 |
143 | ## License
144 |
145 | All source code is licensed under Apache 2.0 license.
146 | You can get a copy at http://www.apache.org/licenses/LICENSE-2.0.html
147 |
148 | ## Packages
149 |
150 | * Archlinux: https://aur.archlinux.org/packages/redsocks-git
151 | * Debian: http://packages.debian.org/search?searchon=names&keywords=redsocks
152 | * Gentoo (zugaina overlay): http://gpo.zugaina.org/net-proxy/redsocks
153 | * Gentoo: https://packages.gentoo.org/packages/net-proxy/redsocks
154 | * Ubuntu: http://packages.ubuntu.com/search?searchon=names&keywords=redsocks
155 |
156 | ## Compilation
157 |
158 | [libevent-2.0.x](http://libevent.org/) is required.
159 |
160 | gcc and clang are supported right now, other compilers can be used
161 | but may require some code changes.
162 |
163 | Compilation is as easy as running `make`, there is no `./configure` magic.
164 |
165 | GNU Make works, other implementations of make were not tested.
166 |
167 | ## Running
168 |
169 | Program has following command-line options:
170 |
171 | * `-c` sets proper path to config file ("./redsocks.conf" is default one)
172 | * `-t` tests config file syntax
173 | * `-p` set a file to write the getpid() into
174 |
175 | Following signals are understood:
176 | SIGUSR1 dumps list of connected clients to log,
177 | SIGTERM and SIGINT terminates daemon, all active connections are closed.
178 |
179 | You can see configuration file example in [redsocks.conf.example](https://github.com/darkk/redsocks/blob/master/redsocks.conf.example).
180 |
181 | ### iptables example
182 |
183 | You have to build iptables with connection tracking and REDIRECT target.
184 |
185 | ```
186 | # Create new chain
187 | root# iptables -t nat -N REDSOCKS
188 |
189 | # Ignore LANs and some other reserved addresses.
190 | # See http://en.wikipedia.org/wiki/Reserved_IP_addresses#Reserved_IPv4_addresses
191 | # and http://tools.ietf.org/html/rfc5735 for full list of reserved networks.
192 | root# iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN
193 | root# iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN
194 | root# iptables -t nat -A REDSOCKS -d 100.64.0.0/10 -j RETURN
195 | root# iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN
196 | root# iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN
197 | root# iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN
198 | root# iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN
199 | root# iptables -t nat -A REDSOCKS -d 198.18.0.0/15 -j RETURN
200 | root# iptables -t nat -A REDSOCKS -d 224.0.0.0/4 -j RETURN
201 | root# iptables -t nat -A REDSOCKS -d 240.0.0.0/4 -j RETURN
202 |
203 | # Anything else should be redirected to port 12345
204 | root# iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345
205 |
206 | # Any tcp connection made by `luser' should be redirected.
207 | root# iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner luser -j REDSOCKS
208 |
209 | # You can also control that in more precise way using `gid-owner` from
210 | # iptables.
211 | root# groupadd socksified
212 | root# usermod --append --groups socksified luser
213 | root# iptables -t nat -A OUTPUT -p tcp -m owner --gid-owner socksified -j REDSOCKS
214 |
215 | # Now you can launch your specific application with GID `socksified` and it
216 | # will be... socksified. See following commands (numbers may vary).
217 | # Note: you may have to relogin to apply `usermod` changes.
218 | luser$ id
219 | uid=1000(luser) gid=1000(luser) groups=1000(luser),1001(socksified)
220 | luser$ sg socksified -c id
221 | uid=1000(luser) gid=1001(socksified) groups=1000(luser),1001(socksified)
222 | luser$ sg socksified -c "firefox"
223 |
224 | # If you want to configure socksifying router, you should look at
225 | # doc/iptables-packet-flow.png, doc/iptables-packet-flow-ng.png and
226 | # https://en.wikipedia.org/wiki/File:Netfilter-packet-flow.svg
227 | # Note, you should have proper `local_ip' value to get external packets with
228 | # redsocks, default 127.0.0.1 will not go. See iptables(8) manpage regarding
229 | # REDIRECT target for details.
230 | # Depending on your network configuration iptables conf. may be as easy as:
231 | root# iptables -t nat -A PREROUTING --in-interface eth_int -p tcp -j REDSOCKS
232 | ```
233 |
234 | ### Note about GID-based redirection
235 |
236 | Keep in mind, that changed GID affects filesystem permissions, so if your
237 | application creates some files, the files will be created with luser:socksified
238 | owner/group. So, if you're not the only user in the group `socksified` and your
239 | umask allows to create group-readable files and your directory permissions, and
240 | so on, blah-blah, etc. THEN you may expose your files to another user.
241 | Ok, you have been warned.
242 |
243 | ## Homepage
244 |
245 | http://darkk.net.ru/redsocks/
246 |
247 | Mailing list: [redsocks@librelist.com](mailto:redsocks@librelist.com).
248 |
249 | Mailing list also has [archives](http://librelist.com/browser/redsocks/).
250 |
251 | ## Author
252 |
253 | This program was written by Leonid Evdokimov
254 |
--------------------------------------------------------------------------------
/apple.c:
--------------------------------------------------------------------------------
1 | #ifdef __APPLE__
2 | #include
3 | #include
4 |
5 | int pipe2(int pipefd[2], int flags)
6 | {
7 | return 0;
8 | }
9 | ssize_t splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t len, unsigned int flags)
10 | {
11 | return 0;
12 | }
13 |
14 | #endif
--------------------------------------------------------------------------------
/apple.h:
--------------------------------------------------------------------------------
1 | #ifndef APPLE_H_SAT_DEC_17_46_12_07_2019
2 | #define APPLE_H_SAT_DEC_17_46_12_07_2019
3 |
4 | #ifdef __APPLE__
5 | #define USE_OWNTIMER 1
6 | #endif
7 |
8 | #ifdef USE_OWNTIMER
9 | #include "timer.h"
10 | #else
11 | #define Timersub timersub
12 | #define Timeradd timeradd
13 | #define Timercmp timercmp
14 | #endif
15 |
16 | #ifndef CMSG_LEN
17 | #define CMSG_LEN(l) (__DARWIN_ALIGN32(sizeof(struct cmsghdr)) + (l))
18 | #endif
19 |
20 | #ifndef MSG_FASTOPEN
21 | #define MSG_FASTOPEN 0
22 | #endif
23 |
24 | #ifndef SOL_IP
25 | #define SOL_IP IPPROTO_IP
26 | #endif
27 |
28 | #ifndef INADDR_LOOPBACK
29 | #define INADDR_LOOPBACK ((unsigned long) 0x7f000001)
30 | #endif
31 |
32 | /*
33 | * Even though Mac OS X does not support the splice implementation of
34 | * Linux, define the associated flags to avoid undeclared identifier
35 | * errors.
36 | */
37 | #ifndef SPLICE_F_MOVE
38 | #define SPLICE_F_MOVE 0x01
39 | #endif
40 | #ifndef SPLICE_F_NONBLOCK
41 | #define SPLICE_F_NONBLOCK 0x02
42 | #endif
43 | #ifndef SPLICE_F_MORE
44 | #define SPLICE_F_MORE 0x04
45 | #endif
46 | #ifndef SPLICE_F_GIFT
47 | #define SPLICE_F_GIFT 0x08
48 | #endif
49 |
50 | #endif
--------------------------------------------------------------------------------
/base.h:
--------------------------------------------------------------------------------
1 | #ifndef BASE_H_SUN_JUN__3_20_15_57_2007
2 | #define BASE_H_SUN_JUN__3_20_15_57_2007
3 |
4 | int getdestaddr(int fd, const struct sockaddr_in *client, const struct sockaddr_in *bindaddr, struct sockaddr_in *destaddr);
5 | int apply_tcp_keepalive(int fd);
6 |
7 | uint32_t redsocks_conn_max();
8 | uint32_t connpres_idle_timeout();
9 | uint32_t max_accept_backoff_ms();
10 |
11 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
12 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
13 | #endif /* BASE_H_SUN_JUN__3_20_15_57_2007 */
14 |
--------------------------------------------------------------------------------
/base64.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
3 | *
4 | * This file is part of FFmpeg.
5 | *
6 | * FFmpeg is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 2.1 of the License, or (at your option) any later version.
10 | *
11 | * FFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public
17 | * License along with FFmpeg; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 |
21 | /**
22 | * @file
23 | * @brief Base64 encode/decode
24 | * @author Ryan Martell (with lots of Michael)
25 | */
26 |
27 | #include
28 | #include "utils.h"
29 | #include "base64.h"
30 |
31 | /* ---------------- private code */
32 | static const uint8_t map2[] =
33 | {
34 | 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36,
35 | 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
36 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01,
37 | 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
38 | 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
39 | 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
40 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b,
41 | 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
42 | 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
43 | 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
44 | };
45 |
46 | int base64_decode(uint8_t *out, const char *in, int out_size)
47 | {
48 | int i, v;
49 | uint8_t *dst = out;
50 |
51 | v = 0;
52 | for (i = 0; in[i] && in[i] != '='; i++) {
53 | unsigned int index= in[i]-43;
54 | if (index>=SIZEOF_ARRAY(map2) || map2[index] == 0xff)
55 | return -1;
56 | v = (v << 6) + map2[index];
57 | if (i & 3) {
58 | if (dst - out < out_size) {
59 | *dst++ = v >> (6 - 2 * (i & 3));
60 | }
61 | }
62 | }
63 |
64 | return dst - out;
65 | }
66 |
67 | /*****************************************************************************
68 | * b64_encode: Stolen from VLC's http.c.
69 | * Simplified by Michael.
70 | * Fixed edge cases and made it work from data (vs. strings) by Ryan.
71 | *****************************************************************************/
72 |
73 | char *base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
74 | {
75 | static const char b64[] =
76 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
77 | char *ret, *dst;
78 | unsigned i_bits = 0;
79 | int i_shift = 0;
80 | int bytes_remaining = in_size;
81 |
82 | if (in_size >= UINT_MAX / 4 ||
83 | out_size < BASE64_SIZE(in_size))
84 | return NULL;
85 | ret = dst = out;
86 | while (bytes_remaining) {
87 | i_bits = (i_bits << 8) + *in++;
88 | bytes_remaining--;
89 | i_shift += 8;
90 |
91 | do {
92 | *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
93 | i_shift -= 6;
94 | } while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0));
95 | }
96 | while ((dst - ret) & 3)
97 | *dst++ = '=';
98 | *dst = '\0';
99 |
100 | return ret;
101 | }
102 |
--------------------------------------------------------------------------------
/base64.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
3 | *
4 | * This file is part of FFmpeg.
5 | *
6 | * FFmpeg is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 2.1 of the License, or (at your option) any later version.
10 | *
11 | * FFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public
17 | * License along with FFmpeg; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 |
21 | #ifndef BASE64_H
22 | #define BASE64_H
23 |
24 | #include
25 |
26 | /**
27 | * Decode a base64-encoded string.
28 | *
29 | * @param out buffer for decoded data
30 | * @param in null-terminated input string
31 | * @param out_size size in bytes of the out buffer, must be at
32 | * least 3/4 of the length of in
33 | * @return number of bytes written, or a negative value in case of
34 | * invalid input
35 | */
36 | int base64_decode(uint8_t *out, const char *in, int out_size);
37 |
38 | /**
39 | * Encode data to base64 and null-terminate.
40 | *
41 | * @param out buffer for encoded data
42 | * @param out_size size in bytes of the output buffer, must be at
43 | * least BASE64_SIZE(in_size)
44 | * @param in_size size in bytes of the 'in' buffer
45 | * @return 'out' or NULL in case of error
46 | */
47 | char *base64_encode(char *out, int out_size, const uint8_t *in, int in_size);
48 |
49 | /**
50 | * Calculate the output size needed to base64-encode x bytes.
51 | */
52 | #define BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1)
53 |
54 | #endif /* BASE64_H */
55 |
--------------------------------------------------------------------------------
/debian/changelog:
--------------------------------------------------------------------------------
1 | redsocks (0.2-1) unstable; urgency=low
2 |
3 | * Initial release (Closes: #649309)
4 |
5 | -- Apollon Oikonomopoulos Mon, 14 Nov 2011 14:42:55 +0200
6 |
--------------------------------------------------------------------------------
/debian/compat:
--------------------------------------------------------------------------------
1 | 8
2 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: redsocks
2 | Section: net
3 | Priority: extra
4 | Maintainer: Apollon Oikonomopoulos
5 | Build-Depends: debhelper (>= 8.0.0), libevent-dev
6 | Standards-Version: 3.9.2
7 | Homepage: http://darkk.net.ru/redsocks/
8 | #Vcs-Git: git://git.debian.org/collab-maint/redsocks.git
9 | #Vcs-Browser: http://git.debian.org/?p=collab-maint/redsocks.git;a=summary
10 |
11 | Package: redsocks
12 | Architecture: any
13 | Depends: ${shlibs:Depends}, ${misc:Depends}, adduser
14 | Description: Redirect any TCP connection to a SOCKS or HTTPS proxy server
15 | Redsocks is a daemon running on the local system, that will transparently
16 | tunnel any TCP connection via a remote SOCKS4, SOCKS5 or HTTP proxy server. It
17 | uses the system firewall's redirection facility to intercept TCP connections,
18 | thus the redirection is system-wide, with fine-grained control, and does
19 | not depend on LD_PRELOAD libraries.
20 | .
21 | Redsocks supports tunneling TCP connections and UDP packets. It has
22 | authentication support for both, SOCKS and HTTP proxies.
23 | .
24 | Also included is a small DNS server returning answers with the "truncated" flag
25 | set for any UDP query, forcing the resolver to use TCP.
26 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format: http://dep.debian.net/deps/dep5
2 | Upstream-Name: redsocks
3 | Source: http://darkk.net.ru/redsocks/
4 |
5 | Files: *
6 | Copyright: 2007-2018 Leonid Evdokimov
7 | License: Apache 2.0
8 | Licensed under the Apache License, Version 2.0 (the "License");
9 | you may not use this file except in compliance with the License.
10 | You may obtain a copy of the License at
11 | .
12 | http://www.apache.org/licenses/LICENSE-2.0
13 | .
14 | Unless required by applicable law or agreed to in writing, software
15 | distributed under the License is distributed on an "AS IS" BASIS,
16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | See the License for the specific language governing permissions and
18 | limitations under the License.
19 | .
20 | On Debian systems, the complete text of the Apache License 2.0 can
21 | be found in "/usr/share/common-licenses/Apache-2.0"
22 |
23 | Files: md5.*
24 | Copyright: 1999, 2002 Aladdin Enterprises
25 | License: Zlib
26 | This software is provided 'as-is', without any express or implied
27 | warranty. In no event will the authors be held liable for any damages
28 | arising from the use of this software.
29 | .
30 | Permission is granted to anyone to use this software for any purpose,
31 | including commercial applications, and to alter it and redistribute it
32 | freely, subject to the following restrictions:
33 | .
34 | 1. The origin of this software must not be misrepresented; you must not
35 | claim that you wrote the original software. If you use this software
36 | in a product, an acknowledgment in the product documentation would be
37 | appreciated but is not required.
38 | 2. Altered source versions must be plainly marked as such, and must not be
39 | misrepresented as being the original software.
40 | 3. This notice may not be removed or altered from any source distribution.
41 |
42 | Files: base64.*
43 | Copyright: 2006 Ryan Martell
44 | License: LGPL-2.1+
45 | FFmpeg is free software; you can redistribute it and/or
46 | modify it under the terms of the GNU Lesser General Public
47 | License as published by the Free Software Foundation; either
48 | version 2.1 of the License, or (at your option) any later version.
49 | .
50 | FFmpeg is distributed in the hope that it will be useful,
51 | but WITHOUT ANY WARRANTY; without even the implied warranty of
52 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
53 | Lesser General Public License for more details.
54 | .
55 | You should have received a copy of the GNU Lesser General Public
56 | License along with FFmpeg; if not, write to the Free Software
57 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
58 | .
59 | On Debian systems, the complete text of the GNU Lesser General Public
60 | License can be found in "/usr/share/common-licenses/LGPL".
61 |
62 | Files: debian/*
63 | Copyright: 2011 Apollon Oikonomopoulos
64 | License:
65 | Copying and distribution of this package, with or without
66 | modification, are permitted in any medium without royalty
67 | provided the copyright notice and this notice are
68 | preserved.
69 |
--------------------------------------------------------------------------------
/debian/init.d:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ### BEGIN INIT INFO
3 | # Provides: redsocks
4 | # Required-Start: $network $local_fs $remote_fs
5 | # Required-Stop: $remote_fs
6 | # Default-Start: 2 3 4 5
7 | # Default-Stop: 0 1 6
8 | # Short-Description: redsocks TCP connection-to-SOCKS redirector
9 | # Description:
10 | # <...>
11 | # <...>
12 | ### END INIT INFO
13 |
14 | # Author: Apollon Oikonomopoulos
15 |
16 | # PATH should only include /usr/* if it runs after the mountnfs.sh script
17 | PATH=/sbin:/usr/sbin:/bin:/usr/bin
18 | DESC=redsocks # Introduce a short description here
19 | NAME=redsocks # Introduce the short server's name here
20 | DAEMON=/usr/sbin/redsocks # Introduce the server's location here
21 | DAEMON_ARGS="" # Arguments to run the daemon with
22 | PIDFILE=/var/run/$NAME/$NAME.pid
23 | SCRIPTNAME=/etc/init.d/$NAME
24 |
25 | # Exit if the package is not installed
26 | [ -x $DAEMON ] || exit 0
27 |
28 | # Read configuration variable file if it is present
29 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME
30 |
31 | [ "$START" = "yes" ] || exit 0
32 |
33 | # Load the VERBOSE setting and other rcS variables
34 | . /lib/init/vars.sh
35 |
36 | # Define LSB log_* functions.
37 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
38 | . /lib/lsb/init-functions
39 |
40 | #
41 | # Function that starts the daemon/service
42 | #
43 | do_start()
44 | {
45 | # Take care of pidfile permissions
46 | USER=$(egrep '\buser\s*=' $CONFFILE | sed -r 's/.*\buser\s+=\s*(.*);/\1/')
47 | mkdir /var/run/$NAME 2>/dev/null || true
48 | chown "$USER" /var/run/$NAME
49 |
50 | # Return
51 | # 0 if daemon has been started
52 | # 1 if daemon was already running
53 | # 2 if daemon could not be started
54 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
55 | || return 1
56 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
57 | -c "$CONFFILE" -p $PIDFILE \
58 | || return 2
59 | # Add code here, if necessary, that waits for the process to be ready
60 | # to handle requests from services started subsequently which depend
61 | # on this one. As a last resort, sleep for some time.
62 | }
63 |
64 | #
65 | # Function that stops the daemon/service
66 | #
67 | do_stop()
68 | {
69 | # Return
70 | # 0 if daemon has been stopped
71 | # 1 if daemon was already stopped
72 | # 2 if daemon could not be stopped
73 | # other if a failure occurred
74 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
75 | RETVAL="$?"
76 | [ "$RETVAL" = 2 ] && return 2
77 | # Wait for children to finish too if this is a daemon that forks
78 | # and if the daemon is only ever run from this initscript.
79 | # If the above conditions are not satisfied then add some other code
80 | # that waits for the process to drop all resources that could be
81 | # needed by services started subsequently. A last resort is to
82 | # sleep for some time.
83 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
84 | [ "$?" = 2 ] && return 2
85 | # Many daemons don't delete their pidfiles when they exit.
86 | rm -f $PIDFILE
87 | return "$RETVAL"
88 | }
89 |
90 |
91 | case "$1" in
92 | start)
93 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
94 | do_start
95 | case "$?" in
96 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
97 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
98 | esac
99 | ;;
100 | stop)
101 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
102 | do_stop
103 | case "$?" in
104 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
105 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
106 | esac
107 | ;;
108 | status)
109 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
110 | ;;
111 | restart|force-reload)
112 | log_daemon_msg "Restarting $DESC" "$NAME"
113 | do_stop
114 | case "$?" in
115 | 0|1)
116 | do_start
117 | case "$?" in
118 | 0) log_end_msg 0 ;;
119 | 1) log_end_msg 1 ;; # Old process is still running
120 | *) log_end_msg 1 ;; # Failed to start
121 | esac
122 | ;;
123 | *)
124 | # Failed to stop
125 | log_end_msg 1
126 | ;;
127 | esac
128 | ;;
129 | *)
130 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
131 | exit 3
132 | ;;
133 | esac
134 |
135 | :
136 |
--------------------------------------------------------------------------------
/debian/postinst:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # postinst script for redsocks
3 | #
4 | # see: dh_installdeb(1)
5 |
6 | set -e
7 |
8 | # summary of how this script can be called:
9 | # * `configure'
10 | # * `abort-upgrade'
11 | # * `abort-remove' `in-favour'
12 | #
13 | # * `abort-remove'
14 | # * `abort-deconfigure' `in-favour'
15 | # `removing'
16 | #
17 | # for details, see http://www.debian.org/doc/debian-policy/ or
18 | # the debian-policy package
19 |
20 |
21 | case "$1" in
22 | configure)
23 | adduser --system --disabled-password --disabled-login --home /var/run/redsocks \
24 | --no-create-home --quiet --group redsocks
25 | ;;
26 |
27 | abort-upgrade|abort-remove|abort-deconfigure)
28 | ;;
29 |
30 | *)
31 | echo "postinst called with unknown argument \`$1'" >&2
32 | exit 1
33 | ;;
34 | esac
35 |
36 | # dh_installdeb will replace this with shell code automatically
37 | # generated by other debhelper scripts.
38 |
39 | #DEBHELPER#
40 |
41 | exit 0
42 |
--------------------------------------------------------------------------------
/debian/postrm:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # postrm script for redsocks
3 | #
4 | # see: dh_installdeb(1)
5 |
6 | set -e
7 |
8 | # summary of how this script can be called:
9 | # * `remove'
10 | # * `purge'
11 | # * `upgrade'
12 | # * `failed-upgrade'
13 | # * `abort-install'
14 | # * `abort-install'
15 | # * `abort-upgrade'
16 | # * `disappear'
17 | #
18 | # for details, see http://www.debian.org/doc/debian-policy/ or
19 | # the debian-policy package
20 |
21 |
22 | case "$1" in
23 | purge)
24 | deluser --system redsocks || true
25 | delgroup --system redsocks || true
26 | ;;
27 | remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
28 | ;;
29 |
30 | *)
31 | echo "postrm called with unknown argument \`$1'" >&2
32 | exit 1
33 | ;;
34 | esac
35 |
36 | # dh_installdeb will replace this with shell code automatically
37 | # generated by other debhelper scripts.
38 |
39 | #DEBHELPER#
40 |
41 | exit 0
42 |
--------------------------------------------------------------------------------
/debian/redsocks.8:
--------------------------------------------------------------------------------
1 | .\" Hey, EMACS: -*- nroff -*-
2 | .\" First parameter, NAME, should be all caps
3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
4 | .\" other parameters are allowed: see man(7), man(1)
5 | .TH REDSOCKS 8 "November 14, 2011"
6 | .\" Please adjust this date whenever revising the manpage.
7 | .\"
8 | .\" Some roff macros, for reference:
9 | .\" .nh disable hyphenation
10 | .\" .hy enable hyphenation
11 | .\" .ad l left justify
12 | .\" .ad b justify to both left and right margins
13 | .\" .nf disable filling
14 | .\" .fi enable filling
15 | .\" .br insert line break
16 | .\" .sp insert n+1 empty lines
17 | .\" for manpage-specific macros, see man(7)
18 | .SH NAME
19 | redsocks \- rediect any TCP connection to a SOCKS or HTTP proxy
20 | .SH SYNOPSIS
21 | .B redsocks
22 | .RI [ options ]
23 | .SH DESCRIPTION
24 | \fBredsocks\fP is a daemon running on the local system, that will transparently
25 | tunnel any TCP connection via a remote SOCKS4, SOCKS5 or HTTP proxy server. It
26 | uses the system firewall's redirection facility to intercept TCP connections,
27 | thus the redirection is system-wide, with fine-grained control, and does
28 | not depend on LD_PRELOAD libraries.
29 | .PP
30 | Redsocks supports tunneling TCP connections and UDP packets. It has
31 | authentication support for both, SOCKS and HTTP proxies.
32 | .PP
33 | Also included is a small DNS server returning answers with the "truncated" flag
34 | set for any UDP query, forcing the resolver to use TCP.
35 | .SH OPTIONS
36 | .TP
37 | .B \-p pidfile
38 | Write the process ID to \fIpidfile\fP.
39 | .TP
40 | .B \-c config
41 | Use \fIconfig\fP as configuration file.
42 | .TP
43 | .B \-t
44 | Test configuration file syntax.
45 | .SH SEE ALSO
46 | .BR iptables (8),
47 | /etc/redsocks.conf
48 | .br
49 | .SH AUTHOR
50 | redsocks was written by Leonid Evdokimov
51 | .PP
52 | This manual page was written by Apollon Oikonomopoulos ,
53 | for the Debian project (and may be used by others).
54 |
--------------------------------------------------------------------------------
/debian/redsocks.conf:
--------------------------------------------------------------------------------
1 | base {
2 | // debug: connection progress & client list on SIGUSR1
3 | log_debug = off;
4 |
5 | // info: start and end of client session
6 | log_info = on;
7 |
8 | /* possible `log' values are:
9 | * stderr
10 | * "file:/path/to/file"
11 | * syslog:FACILITY facility is any of "daemon", "local0"..."local7"
12 | */
13 | log = "syslog:daemon";
14 |
15 | // detach from console
16 | daemon = on;
17 |
18 | /* Change uid, gid and root directory, these options require root
19 | * privilegies on startup.
20 | * Note, your chroot may requre /etc/localtime if you write log to syslog.
21 | * Log is opened before chroot & uid changing.
22 | */
23 | user = redsocks;
24 | group = redsocks;
25 | // chroot = "/var/chroot";
26 |
27 | /* possible `redirector' values are:
28 | * iptables - for Linux
29 | * ipf - for FreeBSD
30 | * pf - for OpenBSD
31 | * generic - some generic redirector that MAY work
32 | */
33 | redirector = iptables;
34 | }
35 |
36 | redsocks {
37 | /* `local_ip' defaults to 127.0.0.1 for security reasons,
38 | * use 0.0.0.0 if you want to listen on every interface.
39 | * `local_*' are used as port to redirect to.
40 | */
41 | local_ip = 127.0.0.1;
42 | local_port = 12345;
43 |
44 | // `ip' and `port' are IP and tcp-port of proxy-server
45 | // You can also use hostname instead of IP, only one (random)
46 | // address of multihomed host will be used.
47 | ip = 127.0.0.1;
48 | port = 1080;
49 |
50 |
51 | // known types: socks4, socks5, http-connect, http-relay
52 | type = socks5;
53 |
54 | // login = "foobar";
55 | // password = "baz";
56 | }
57 |
58 | redudp {
59 | // `local_ip' should not be 0.0.0.0 as it's also used for outgoing
60 | // packets that are sent as replies - and it should be fixed
61 | // if we want NAT to work properly.
62 | local_ip = 127.0.0.1;
63 | local_port = 10053;
64 |
65 | // `ip' and `port' of socks5 proxy server.
66 | ip = 10.0.0.1;
67 | port = 1080;
68 | login = username;
69 | password = pazzw0rd;
70 |
71 | // kernel does not give us this information, so we have to duplicate it
72 | // in both iptables rules and configuration file. By the way, you can
73 | // set `local_ip' to 127.45.67.89 if you need more than 65535 ports to
74 | // forward ;-)
75 | // This limitation may be relaxed in future versions using contrack-tools.
76 | dest_ip = 8.8.8.8;
77 | dest_port = 53;
78 |
79 | udp_timeout = 30;
80 | udp_timeout_stream = 180;
81 | }
82 |
83 | dnstc {
84 | // fake and really dumb DNS server that returns "truncated answer" to
85 | // every query via UDP, RFC-compliant resolver should repeat same query
86 | // via TCP in this case.
87 | local_ip = 127.0.0.1;
88 | local_port = 5300;
89 | }
90 |
91 | // you can add more `redsocks' and `redudp' sections if you need.
92 |
--------------------------------------------------------------------------------
/debian/redsocks.default:
--------------------------------------------------------------------------------
1 | # Defaults for redsocks initscript
2 | # sourced by /etc/init.d/redsocks
3 | # installed at /etc/default/redsocks by the maintainer scripts
4 |
5 | #
6 | # This is a POSIX shell fragment
7 | #
8 |
9 | # Enable during startup?
10 | START=no
11 |
12 | # Configuration file
13 | CONFFILE="/etc/redsocks.conf"
14 |
--------------------------------------------------------------------------------
/debian/redsocks.docs:
--------------------------------------------------------------------------------
1 | README
2 | README.html
3 |
--------------------------------------------------------------------------------
/debian/redsocks.install:
--------------------------------------------------------------------------------
1 | redsocks /usr/sbin
2 | debian/redsocks.conf /etc
3 |
--------------------------------------------------------------------------------
/debian/redsocks.manpages:
--------------------------------------------------------------------------------
1 | debian/redsocks.8
2 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | # -*- makefile -*-
3 | # Sample debian/rules that uses debhelper.
4 | # This file was originally written by Joey Hess and Craig Small.
5 | # As a special exception, when this file is copied by dh-make into a
6 | # dh-make output file, you may use that output file without restriction.
7 | # This special exception was added by Craig Small in version 0.37 of dh-make.
8 |
9 | # Uncomment this to turn on verbose mode.
10 | #export DH_VERBOSE=1
11 |
12 | %:
13 | dh $@
14 |
--------------------------------------------------------------------------------
/debian/source/format:
--------------------------------------------------------------------------------
1 | 3.0 (quilt)
2 |
--------------------------------------------------------------------------------
/debian/watch:
--------------------------------------------------------------------------------
1 | version=3
2 |
3 | # Use githubredir.debian.net for the time being
4 | http://githubredir.debian.net/github/darkk/redsocks (.*).tar.gz
5 |
--------------------------------------------------------------------------------
/debug.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | #include "parser.h"
18 | #include "main.h"
19 | #include "log.h"
20 | #include "utils.h"
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | typedef struct debug_instance_t {
28 | int configured;
29 | char* http_ip;
30 | uint16_t http_port;
31 | struct evhttp* http_server;
32 | } debug_instance;
33 |
34 | static debug_instance instance = {
35 | .configured = 0,
36 | };
37 |
38 | static parser_entry debug_entries[] =
39 | {
40 | { .key = "http_ip", .type = pt_pchar },
41 | { .key = "http_port", .type = pt_uint16 },
42 | { }
43 | };
44 |
45 | static int debug_onenter(parser_section *section)
46 | {
47 | if (instance.configured) {
48 | parser_error(section->context, "only one instance of debug is valid");
49 | return -1;
50 | }
51 | memset(&instance, 0, sizeof(instance));
52 | for (parser_entry *entry = §ion->entries[0]; entry->key; entry++)
53 | entry->addr =
54 | (strcmp(entry->key, "http_ip") == 0) ? (void*)&instance.http_ip:
55 | (strcmp(entry->key, "http_port") == 0) ? (void*)&instance.http_port :
56 | NULL;
57 | return 0;
58 | }
59 |
60 | static int debug_onexit(parser_section *section)
61 | {
62 | instance.configured = 1;
63 | if (!instance.http_ip)
64 | instance.http_ip = strdup("localhost");
65 | return 0;
66 | }
67 |
68 | static parser_section debug_conf_section =
69 | {
70 | .name = "debug",
71 | .entries = debug_entries,
72 | .onenter = debug_onenter,
73 | .onexit = debug_onexit,
74 | };
75 |
76 | static void debug_pipe(struct evhttp_request *req, void *arg)
77 | {
78 | UNUSED(arg);
79 | int fds[2];
80 | int code = (pipe(fds) == 0) ? HTTP_OK : HTTP_SERVUNAVAIL;
81 | evhttp_send_reply(req, code, NULL, NULL);
82 | }
83 | static void debug_meminfo_json(struct evhttp_request *req, void *arg)
84 | {
85 | UNUSED(arg);
86 |
87 | struct evbuffer* body = evbuffer_new();
88 | evbuffer_add(body, "{", 1);
89 |
90 | FILE* fd = fopen("/proc/vmstat", "r");
91 | while (!feof(fd)) {
92 | char buf[64];
93 | size_t pages;
94 | if (fscanf(fd, "%63s %zu", buf, &pages) == 2 && strncmp(buf, "nr_", 3) == 0) {
95 | evbuffer_add_printf(body, "\"%s\": %zu, ", buf, pages);
96 | }
97 | for (int c = 0; c != EOF && c != '\n'; c = fgetc(fd))
98 | ;
99 | }
100 | fclose(fd);
101 |
102 | size_t vmsize, rss, share, text, z, data;
103 | fd = fopen("/proc/self/statm", "r");
104 | if (fscanf(fd, "%zu %zu %zu %zu %zu %zu %zu", &vmsize, &rss, &share, &text, &z, &data, &z) == 7) {
105 | evbuffer_add_printf(body,
106 | "\"vmsize\": %zu, \"vmrss\": %zu, \"share\": %zu, \"text\": %zu, \"data\": %zu, ",
107 | vmsize, rss, share, text, data);
108 |
109 | }
110 | fclose(fd);
111 | evbuffer_add_printf(body, "\"getpagesize\": %d}", getpagesize());
112 |
113 | evhttp_send_reply(req, HTTP_OK, NULL, body);
114 | evbuffer_free(body);
115 | }
116 |
117 | static int debug_fini();
118 |
119 | static int debug_init(struct event_base* evbase)
120 | {
121 | if (!instance.http_port)
122 | return 0;
123 |
124 | instance.http_server = evhttp_new(evbase);
125 | if (!instance.http_server) {
126 | log_errno(LOG_ERR, "evhttp_new()");
127 | goto fail;
128 | }
129 |
130 | if (evhttp_bind_socket(instance.http_server, instance.http_ip, instance.http_port) != 0) {
131 | log_errno(LOG_ERR, "evhttp_bind_socket()");
132 | goto fail;
133 | }
134 |
135 | if (evhttp_set_cb(instance.http_server, "/debug/pipe", debug_pipe, NULL) != 0) {
136 | log_errno(LOG_ERR, "evhttp_set_cb()");
137 | goto fail;
138 | }
139 |
140 | if (evhttp_set_cb(instance.http_server, "/debug/meminfo.json", debug_meminfo_json, NULL) != 0) {
141 | log_errno(LOG_ERR, "evhttp_set_cb()");
142 | goto fail;
143 | }
144 |
145 | return 0;
146 |
147 | fail:
148 | debug_fini();
149 | return -1;
150 | }
151 |
152 | static int debug_fini()
153 | {
154 | if (instance.http_server) {
155 | evhttp_free(instance.http_server);
156 | instance.http_server = 0;
157 | }
158 | free(instance.http_ip);
159 | memset(&instance, 0, sizeof(instance));
160 | return 0;
161 | }
162 |
163 | app_subsys debug_subsys =
164 | {
165 | .init = debug_init,
166 | .fini = debug_fini,
167 | .conf_section = &debug_conf_section,
168 | };
169 |
170 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
171 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
172 |
--------------------------------------------------------------------------------
/dnstc.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #include "list.h"
28 | #include "log.h"
29 | #include "parser.h"
30 | #include "main.h"
31 | #include "redsocks.h"
32 | #include "dnstc.h"
33 | #include "utils.h"
34 |
35 | #define dnstc_log_error(prio, msg...) \
36 | redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &clientaddr, &self->config.bindaddr, prio, ## msg)
37 | #define dnstc_log_errno(prio, msg...) \
38 | redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &clientaddr, &self->config.bindaddr, prio, ## msg)
39 |
40 | static void dnstc_fini_instance(dnstc_instance *instance);
41 | static int dnstc_fini();
42 |
43 | typedef struct dns_header_t {
44 | uint16_t id;
45 | uint8_t qr_opcode_aa_tc_rd;
46 | uint8_t ra_z_rcode;
47 | uint16_t qdcount;
48 | uint16_t ancount;
49 | uint16_t nscount;
50 | uint16_t arcount; // may be >0 for EDNS queries
51 | } PACKED dns_header;
52 |
53 | #define DNS_QR 0x80
54 | #define DNS_TC 0x02
55 | #define DNS_Z 0x70
56 |
57 | /***********************************************************************
58 | * Logic
59 | */
60 | static void dnstc_pkt_from_client(int fd, short what, void *_arg)
61 | {
62 | dnstc_instance *self = _arg;
63 | struct sockaddr_in clientaddr;
64 | union {
65 | char raw[0xFFFF]; // UDP packet can't be larger then that
66 | dns_header h;
67 | } buf;
68 | ssize_t pktlen, outgoing;
69 |
70 | assert(fd == event_get_fd(&self->listener));
71 | pktlen = red_recv_udp_pkt(fd, buf.raw, sizeof(buf), &clientaddr, NULL);
72 | if (pktlen == -1)
73 | return;
74 |
75 | if (pktlen <= sizeof(dns_header)) {
76 | dnstc_log_error(LOG_INFO, "incomplete DNS request");
77 | return;
78 | }
79 |
80 | if (1
81 | && (buf.h.qr_opcode_aa_tc_rd & DNS_QR) == 0 /* query */
82 | && (buf.h.ra_z_rcode & DNS_Z) == 0 /* Z is Zero */
83 | && buf.h.qdcount /* some questions */
84 | && !buf.h.ancount && !buf.h.nscount /* no answers */
85 | ) {
86 | buf.h.qr_opcode_aa_tc_rd |= DNS_QR;
87 | buf.h.qr_opcode_aa_tc_rd |= DNS_TC;
88 | outgoing = sendto(fd, buf.raw, pktlen, 0,
89 | (struct sockaddr*)&clientaddr, sizeof(clientaddr));
90 | if (outgoing != pktlen)
91 | dnstc_log_errno(LOG_WARNING, "sendto: I was sending %zd bytes, but only %zd were sent.",
92 | pktlen, outgoing);
93 | else
94 | dnstc_log_error(LOG_INFO, "sent truncated DNS reply");
95 | }
96 | else {
97 | dnstc_log_error(LOG_INFO, "malformed DNS request");
98 | }
99 | }
100 |
101 | /***********************************************************************
102 | * Init / shutdown
103 | */
104 | static parser_entry dnstc_entries[] =
105 | {
106 | { .key = "local_ip", .type = pt_in_addr },
107 | { .key = "local_port", .type = pt_uint16 },
108 | { }
109 | };
110 |
111 | static list_head instances = LIST_HEAD_INIT(instances);
112 |
113 | static int dnstc_onenter(parser_section *section)
114 | {
115 | dnstc_instance *instance = calloc(1, sizeof(*instance));
116 | if (!instance) {
117 | parser_error(section->context, "Not enough memory");
118 | return -1;
119 | }
120 |
121 | INIT_LIST_HEAD(&instance->list);
122 | instance->config.bindaddr.sin_family = AF_INET;
123 | instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
124 |
125 | for (parser_entry *entry = §ion->entries[0]; entry->key; entry++)
126 | entry->addr =
127 | (strcmp(entry->key, "local_ip") == 0) ? (void*)&instance->config.bindaddr.sin_addr :
128 | (strcmp(entry->key, "local_port") == 0) ? (void*)&instance->config.bindaddr.sin_port :
129 | NULL;
130 | section->data = instance;
131 | return 0;
132 | }
133 |
134 | static int dnstc_onexit(parser_section *section)
135 | {
136 | dnstc_instance *instance = section->data;
137 |
138 | section->data = NULL;
139 | for (parser_entry *entry = §ion->entries[0]; entry->key; entry++)
140 | entry->addr = NULL;
141 |
142 | instance->config.bindaddr.sin_port = htons(instance->config.bindaddr.sin_port);
143 |
144 | list_add(&instance->list, &instances);
145 |
146 | return 0;
147 | }
148 |
149 | static int dnstc_init_instance(dnstc_instance *instance)
150 | {
151 | /* FIXME: dnstc_fini_instance is called in case of failure, this
152 | * function will remove instance from instances list - result
153 | * looks ugly.
154 | */
155 | int error;
156 | int fd = -1;
157 |
158 | fd = socket(AF_INET, SOCK_DGRAM, 0);
159 | if (fd == -1) {
160 | log_errno(LOG_ERR, "socket");
161 | goto fail;
162 | }
163 |
164 | error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr));
165 | if (error) {
166 | log_errno(LOG_ERR, "bind");
167 | goto fail;
168 | }
169 |
170 | error = fcntl_nonblock(fd);
171 | if (error) {
172 | log_errno(LOG_ERR, "fcntl");
173 | goto fail;
174 | }
175 |
176 | event_set(&instance->listener, fd, EV_READ | EV_PERSIST, dnstc_pkt_from_client, instance);
177 | error = event_add(&instance->listener, NULL);
178 | if (error) {
179 | log_errno(LOG_ERR, "event_add");
180 | goto fail;
181 | }
182 |
183 | return 0;
184 |
185 | fail:
186 | dnstc_fini_instance(instance);
187 |
188 | if (fd != -1) {
189 | if (close(fd) != 0)
190 | log_errno(LOG_WARNING, "close");
191 | }
192 |
193 | return -1;
194 | }
195 |
196 | /* Drops instance completely, freeing its memory and removing from
197 | * instances list.
198 | */
199 | static void dnstc_fini_instance(dnstc_instance *instance)
200 | {
201 | if (event_initialized(&instance->listener)) {
202 | if (event_del(&instance->listener) != 0)
203 | log_errno(LOG_WARNING, "event_del");
204 | if (close(event_get_fd(&instance->listener)) != 0)
205 | log_errno(LOG_WARNING, "close");
206 | memset(&instance->listener, 0, sizeof(instance->listener));
207 | }
208 |
209 | list_del(&instance->list);
210 |
211 | memset(instance, 0, sizeof(*instance));
212 | free(instance);
213 | }
214 |
215 | static int dnstc_init()
216 | {
217 | dnstc_instance *tmp, *instance = NULL;
218 |
219 | // TODO: init debug_dumper
220 |
221 | list_for_each_entry_safe(instance, tmp, &instances, list) {
222 | if (dnstc_init_instance(instance) != 0)
223 | goto fail;
224 | }
225 |
226 | return 0;
227 |
228 | fail:
229 | dnstc_fini();
230 | return -1;
231 | }
232 |
233 | static int dnstc_fini()
234 | {
235 | dnstc_instance *tmp, *instance = NULL;
236 |
237 | list_for_each_entry_safe(instance, tmp, &instances, list)
238 | dnstc_fini_instance(instance);
239 |
240 | return 0;
241 | }
242 |
243 | static parser_section dnstc_conf_section =
244 | {
245 | .name = "dnstc",
246 | .entries = dnstc_entries,
247 | .onenter = dnstc_onenter,
248 | .onexit = dnstc_onexit
249 | };
250 |
251 | app_subsys dnstc_subsys =
252 | {
253 | .init = dnstc_init,
254 | .fini = dnstc_fini,
255 | .conf_section = &dnstc_conf_section,
256 | };
257 |
258 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
259 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
260 |
--------------------------------------------------------------------------------
/dnstc.h:
--------------------------------------------------------------------------------
1 | #ifndef DNSTC_H
2 | #define DNSTC_H
3 |
4 | typedef struct dnstc_config_t {
5 | struct sockaddr_in bindaddr;
6 | } dnstc_config;
7 |
8 | typedef struct dnstc_instance_t {
9 | list_head list;
10 | dnstc_config config;
11 | struct event listener;
12 | } dnstc_instance;
13 |
14 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
15 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
16 | #endif /* REDUDP_H */
17 |
--------------------------------------------------------------------------------
/dnsu2t.h:
--------------------------------------------------------------------------------
1 | #ifndef DNSU2T_H
2 | #define DNSU2T_H
3 |
4 | #include "utils.h"
5 |
6 | typedef struct dnsu2t_config_t {
7 | struct sockaddr_in bindaddr;
8 | struct sockaddr_in relayaddr;
9 | uint16_t relay_timeout;
10 | uint16_t inflight_max;
11 | } dnsu2t_config;
12 |
13 | typedef struct dns_header_t {
14 | uint16_t id;
15 | uint8_t qr_opcode_aa_tc_rd;
16 | uint8_t ra_z_rcode;
17 | uint16_t qdcount;
18 | uint16_t ancount;
19 | uint16_t nscount;
20 | uint16_t arcount; // may be >0 for EDNS queries
21 | } PACKED dns_header;
22 |
23 | #define DNS_QR 0x80
24 | #define DNS_TC 0x02
25 | #define DNS_Z 0x70
26 |
27 | typedef struct dns_tcp_pkt_t {
28 | uint16_t sz;
29 | union {
30 | dns_header hdr;
31 | char raw[0xffff];
32 | } dns;
33 | } PACKED dns_tcp_pkt;
34 |
35 | typedef struct dnsu2t_instance_t {
36 | list_head list;
37 | dnsu2t_config config;
38 | struct event listener;
39 |
40 | struct event relay_rd;
41 | struct event relay_wr;
42 |
43 | bool reqstream_broken;
44 | int request_count;
45 | int inflight_count;
46 | void* inflight_root;
47 |
48 | ssize_t pkt_size;
49 | dns_tcp_pkt pkt;
50 | } dnsu2t_instance;
51 |
52 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
53 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
54 | #endif /* DNSU2T_H */
55 |
--------------------------------------------------------------------------------
/doc/balabit-TPROXY-README.txt:
--------------------------------------------------------------------------------
1 |
2 | These are the Transparent Proxying patches for Linux kernel 2.6.
3 |
4 | The latest version can always be found at
5 |
6 | http://www.balabit.com/download/files/tproxy/
7 |
8 |
9 | What does the term 'proxy' mean?
10 | --------------------------------
11 |
12 | A proxy is a server-like program, receiving requests from clients,
13 | forwarding those requests to the real server on behalf of users,
14 | and returning the response as it arrives.
15 |
16 | Proxies read and parse the application protocol, and reject invalid
17 | traffic. As most attacks violate the application protocol, disallowing
18 | protocol violations usually protects against attacks.
19 |
20 | What is transparent proxying?
21 | -----------------------------
22 |
23 | To simplify management tasks of clients sitting behind proxy
24 | firewalls, the technique 'transparent proxying' was invented.
25 | Transparent proxying means that the presence of the proxy is invisible
26 | to the user. Transparent proxying however requires kernel support.
27 |
28 | We have a 'REDIRECT' target, isn't that enough?
29 | ----------------------------------------------
30 |
31 | Real transparent proxying requires the following three features from
32 | the IP stack of the computer it is running on:
33 | 1. Redirect sessions destined to the outer network to a local process
34 | using a packet filter rule.
35 | 2. Make it possible for a process to listen to connections on a
36 | foreign address.
37 | 3. Make it possible for a process to initiate a connection with a
38 | foreign address as a source.
39 |
40 | Item #1 is usually provided by packet filtering packages like
41 | Netfilter/IPTables, IPFilter. (yes, this is the REDIRECT target)
42 |
43 | All three were provided in Linux kernels 2.2.x, but support for this
44 | was removed.
45 |
46 | How to install it?
47 | ------------------
48 |
49 | Download the latest tproxy-kernel-*.tar.bz2 tarball
50 | for your kernel (from v2.6.24), with the tproxy-iptables-*.patch file.
51 |
52 | Patch your kernel using:
53 |
54 | cd /usr/src/linux
55 | cat /00*.patch | patch -p1
56 |
57 | then enable tproxy support, `socket' and `TPROXY' modules
58 | (with optional conntrack support if you need SNAT), compile your kernel
59 | and modules.
60 |
61 | The required modules are automatically loaded if the iptables commands
62 | are used.
63 |
64 | The IPtables patches:
65 |
66 | cd /usr/src/iptables-1.4.X
67 | cat /tproxy-iptables*.patch | patch -p1
68 |
69 | then compile it on the usual way:
70 |
71 | ./autogen.sh
72 | ./configure && make && make install
73 |
74 | Squid-3 has official support of TProxy v4.1:
75 |
76 | checkout the source code of squid-3 as in
77 |
78 | http://wiki.squid-cache.org/Squid3VCS
79 |
80 |
81 | then compile it:
82 |
83 | cd ~/source/squid
84 | ./bootstrap.sh
85 | ./configure --enable-linux-netfilter && make && make install
86 |
87 | Of course you might need to change the path in the examples above.
88 |
89 | How to start using it?
90 | ----------------------
91 |
92 | This implementation of transparent proxying works by marking packets and
93 | changing the route based on packet mark. The foreign address bind and tproxy
94 | redirection is enabled via a new socket option, IP_TRANSPARENT, without it
95 | neither the bind nor the tproxy target works.
96 |
97 | Now let's see what happens when a proxy tries to use the required tproxy
98 | features I outlined earlier.
99 |
100 | 1. Redirection
101 |
102 | This is easy, as this was already supported by iptables. Redirection is
103 | equivalent with the following nat rule:
104 |
105 | iptables -t nat -A PREROUTING -j DNAT --to-dest --to-port
106 |
107 | is one the IP address of the interface where the packet
108 | entered the IP stack
109 | is the port where the proxy was bound to
110 |
111 | To indicate that this is not simple NAT rule, a separate target, 'TPROXY'
112 | was created:
113 |
114 | iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY --on-port \
115 | --tproxy-mark 0x1/0x1
116 |
117 | The local IP address is determined automatically, but can be overridden
118 | by the --on-ip parameter.
119 |
120 | The marked sockets has to be routed locally:
121 |
122 | ip rule add fwmark 1 lookup 100
123 | ip route add local 0.0.0.0/0 dev lo table 100
124 |
125 |
126 | 2. Listening for connections on a foreign address
127 |
128 | There are protocols which use more than a single TCP channel for
129 | communication. The best example is FTP which uses a command channel for
130 | sending commands, and a data channel to transfer the body of files. The
131 | secondary channel can be established in both active and passive mode,
132 | active meaning the server connects back to the client, passive meaning
133 | the client connects to the server on another port.
134 |
135 | Let's see the passive case, when the client establishes a connection to
136 | the address returned in the response of the PASV FTP command.
137 |
138 | As the presence of the proxy is transparent to the client, the target
139 | IP address of the secondary channel (e.g. the address in the PASV
140 | response) is the server (and not the firewall) and this connection must
141 | also be handled by the proxy.
142 |
143 | The first solution that comes to mind is to add a a TPROXY rule
144 | automatically (e.g. to redirect a connection destined to a given server
145 | on a given port to a local process), however it is not feasible, adding
146 | rules on the fly should not be required as it would mess the
147 | administrator's own rules, the NAT translation should be done
148 | implicitly without touching the user rulebase.
149 |
150 | To do this on a Linux 2.2 kernel it was enough to call bind() on a
151 | socket with a foreign IP address, and if a new connection to the given
152 | foreign IP was routed through the firewall the connection was
153 | intercepted. This solution however distracted the core network kernel
154 | hackers and removed this feature. This implementation is similar to
155 | the old behaviour although it works a bit differently:
156 |
157 | * the proxy sets the IP_TRANSPARENT socket option on the listening
158 | socket
159 | * the proxy then binds to the foreign address
160 | * the proxy accepts incoming connections
161 |
162 | It requires additional iptables rules with the socket module of the
163 | tproxy patches:
164 |
165 | iptables -t mangle -N DIVERT
166 | iptables -t mangle -A PREROUTING -p tcpo -m socket -j DIVERT
167 | iptables -t mangle -A DIVERT -j MARK --set-xmark 0x1/0xffffffff
168 | iptables -t mangle -A DIVERT -j ACCEPT
169 |
170 | the best if the second rule is before using the TPROXY target.
171 |
172 | 3. Initiating connections with a foreign address as a source
173 |
174 | Similarly to the case outlined above, it is sometimes necessary to be
175 | able to initiate a connection with a foreign IP address as a source.
176 | Imagine the active FTP case when the FTP client listens for connections
177 | with source address equal to the server. Another example: a webserver
178 | in your DMZ which does access control based on client IP address. If
179 | the proxy could not initiate connections with foreign IP address, the
180 | webserver would see the inner IP address of the firewall itself.
181 |
182 | In Linux 2.2 this was accomplished by bind()-ing to a foreign address
183 | prior calling connect(), and it worked. In this tproxy patch it is done
184 | somewhat similar to the case 2 outlined above.
185 |
186 | * the proxy calls setsockopt with IP_TRANSPARENT
187 |
188 | * the proxy bind to a foreign address
189 |
190 | * the tproxy calls connect()
191 |
192 | The iptables rules with the socket match are also required here.
193 |
194 | How to use it?
195 | --------------
196 |
197 | The following use-case assumes a transparent proxy listening on port
198 | 50080 and any ip address (0.0.0.0).
199 |
200 | First, set up the routing rules with iproute2:
201 |
202 | ip rule add fwmark 1 lookup 100
203 | ip route add local 0.0.0.0/0 dev lo table 100
204 |
205 | Or, if you want to use packet marking for anything else, the least
206 | significant bit is enough for transparent proxying.
207 |
208 | ip rule add fwmark 0x1/0x1 lookup 100
209 | ip route add local 0.0.0.0/0 dev lo table 100
210 |
211 | Note that this latter example is only working with newer versions of
212 | iproute2.
213 |
214 | For supporting foreign address bind, the socket match is required with
215 | packet marking:
216 |
217 | iptables -t mangle -N DIVERT
218 | iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
219 |
220 | # DIVERT chain: mark packets and accept
221 | iptables -t mangle -A DIVERT -j MARK --set-mark 1
222 | iptables -t mangle -A DIVERT -j ACCEPT
223 |
224 | The last rule is for diverting traffic to the proxy:
225 |
226 | iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
227 | --tproxy-mark 0x1/0x1 --on-port 50080
228 |
229 | If it is a Squid-3 proxy, in /etc/squid/squid.conf the following
230 | rule is necessary for transparent proxying:
231 |
232 | http_port 50080 tproxy transparent
233 |
234 | Then set up the ACL rules according to your local policy.
235 |
236 |
--------------------------------------------------------------------------------
/doc/iptables-packet-flow-ng.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samyk/redsocks/1edc820d51a9b9e6829bad8455f39c1244cf706f/doc/iptables-packet-flow-ng.png
--------------------------------------------------------------------------------
/doc/iptables-packet-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samyk/redsocks/1edc820d51a9b9e6829bad8455f39c1244cf706f/doc/iptables-packet-flow.png
--------------------------------------------------------------------------------
/doc/rfc1929-socks5-auth.txt:
--------------------------------------------------------------------------------
1 |
2 | Network Working Group M. Leech
3 | Request for Comments: 1929 Bell-Northern Research Ltd
4 | Category: Standards Track March 1996
5 |
6 | Username/Password Authentication for SOCKS V5
7 |
8 | Status of this Memo
9 |
10 | This document specifies an Internet standards track protocol for the
11 | Internet community, and requests discussion and suggestions for
12 | improvements. Please refer to the current edition of the "Internet
13 | Official Protocol Standards" (STD 1) for the standardization state
14 | and status of this protocol. Distribution of this memo is unlimited.
15 |
16 | 1. Introduction
17 |
18 | The protocol specification for SOCKS Version 5 specifies a
19 | generalized framework for the use of arbitrary authentication
20 | protocols in the initial socks connection setup. This document
21 | describes one of those protocols, as it fits into the SOCKS Version 5
22 | authentication "subnegotiation".
23 |
24 | Note:
25 |
26 | Unless otherwise noted, the decimal numbers appearing in packet-
27 | format diagrams represent the length of the corresponding field, in
28 | octets. Where a given octet must take on a specific value, the
29 | syntax X'hh' is used to denote the value of the single octet in that
30 | field. When the word 'Variable' is used, it indicates that the
31 | corresponding field has a variable length defined either by an
32 | associated (one or two octet) length field, or by a data type field.
33 |
34 | 2. Initial negotiation
35 |
36 | Once the SOCKS V5 server has started, and the client has selected the
37 | Username/Password Authentication protocol, the Username/Password
38 | subnegotiation begins. This begins with the client producing a
39 | Username/Password request:
40 |
41 | +----+------+----------+------+----------+
42 | |VER | ULEN | UNAME | PLEN | PASSWD |
43 | +----+------+----------+------+----------+
44 | | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
45 | +----+------+----------+------+----------+
46 |
47 | Leech Standards Track [Page 1]
48 |
49 | RFC 1929 Username Authentication for SOCKS V5 March 1996
50 |
51 | The VER field contains the current version of the subnegotiation,
52 | which is X'01'. The ULEN field contains the length of the UNAME field
53 | that follows. The UNAME field contains the username as known to the
54 | source operating system. The PLEN field contains the length of the
55 | PASSWD field that follows. The PASSWD field contains the password
56 | association with the given UNAME.
57 |
58 | The server verifies the supplied UNAME and PASSWD, and sends the
59 | following response:
60 |
61 | +----+--------+
62 | |VER | STATUS |
63 | +----+--------+
64 | | 1 | 1 |
65 | +----+--------+
66 |
67 | A STATUS field of X'00' indicates success. If the server returns a
68 | `failure' (STATUS value other than X'00') status, it MUST close the
69 | connection.
70 |
71 | 3. Security Considerations
72 |
73 | This document describes a subnegotiation that provides authentication
74 | services to the SOCKS protocol. Since the request carries the
75 | password in cleartext, this subnegotiation is not recommended for
76 | environments where "sniffing" is possible and practical.
77 |
78 | 4. Author's Address
79 |
80 | Marcus Leech
81 | Bell-Northern Research Ltd
82 | P.O. Box 3511, Station C
83 | Ottawa, ON
84 | CANADA K1Y 4H7
85 |
86 | Phone: +1 613 763 9145
87 | EMail: mleech@bnr.ca
88 |
89 | Leech Standards Track [Page 2]
90 |
--------------------------------------------------------------------------------
/doc/socks4.protocol.txt:
--------------------------------------------------------------------------------
1 | SOCKS: A protocol for TCP proxy across firewalls
2 |
3 | Ying-Da Lee
4 | Principal Member Technical Staff
5 | NEC Systems Laboratory, CSTC
6 | ylee@syl.dl.nec.com
7 |
8 | SOCKS was originally developed by David Koblas and subsequently modified
9 | and extended by me to its current running version -- version 4. It is a
10 | protocol that relays TCP sessions at a firewall host to allow application
11 | users transparent access across the firewall. Because the protocol is
12 | independent of application protocols, it can be (and has been) used for
13 | many different services, such as telnet, ftp, finger, whois, gopher, WWW,
14 | etc. Access control can be applied at the beginning of each TCP session;
15 | thereafter the server simply relays the data between the client and the
16 | application server, incurring minimum processing overhead. Since SOCKS
17 | never has to know anything about the application protocol, it should also
18 | be easy for it to accommodate applications which use encryption to protect
19 | their traffic from nosey snoopers.
20 |
21 | Two operations are defined: CONNECT and BIND.
22 |
23 | 1) CONNECT
24 |
25 | The client connects to the SOCKS server and sends a CONNECT request when
26 | it wants to establish a connection to an application server. The client
27 | includes in the request packet the IP address and the port number of the
28 | destination host, and userid, in the following format.
29 |
30 | +----+----+----+----+----+----+----+----+----+----+....+----+
31 | | VN | CD | DSTPORT | DSTIP | USERID |NULL|
32 | +----+----+----+----+----+----+----+----+----+----+....+----+
33 | # of bytes: 1 1 2 4 variable 1
34 |
35 | VN is the SOCKS protocol version number and should be 4. CD is the
36 | SOCKS command code and should be 1 for CONNECT request. NULL is a byte
37 | of all zero bits.
38 |
39 | The SOCKS server checks to see whether such a request should be granted
40 | based on any combination of source IP address, destination IP address,
41 | destination port number, the userid, and information it may obtain by
42 | consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS
43 | server makes a connection to the specified port of the destination host.
44 | A reply packet is sent to the client when this connection is established,
45 | or when the request is rejected or the operation fails.
46 |
47 | +----+----+----+----+----+----+----+----+
48 | | VN | CD | DSTPORT | DSTIP |
49 | +----+----+----+----+----+----+----+----+
50 | # of bytes: 1 1 2 4
51 |
52 | VN is the version of the reply code and should be 0. CD is the result
53 | code with one of the following values:
54 |
55 | 90: request granted
56 | 91: request rejected or failed
57 | 92: request rejected becasue SOCKS server cannot connect to
58 | identd on the client
59 | 93: request rejected because the client program and identd
60 | report different user-ids
61 |
62 | The remaining fields are ignored.
63 |
64 | The SOCKS server closes its connection immediately after notifying
65 | the client of a failed or rejected request. For a successful request,
66 | the SOCKS server gets ready to relay traffic on both directions. This
67 | enables the client to do I/O on its connection as if it were directly
68 | connected to the application server.
69 |
70 |
71 | 2) BIND
72 |
73 | The client connects to the SOCKS server and sends a BIND request when
74 | it wants to prepare for an inbound connection from an application server.
75 | This should only happen after a primary connection to the application
76 | server has been established with a CONNECT. Typically, this is part of
77 | the sequence of actions:
78 |
79 | -bind(): obtain a socket
80 | -getsockname(): get the IP address and port number of the socket
81 | -listen(): ready to accept call from the application server
82 | -use the primary connection to inform the application server of
83 | the IP address and the port number that it should connect to.
84 | -accept(): accept a connection from the application server
85 |
86 | The purpose of SOCKS BIND operation is to support such a sequence
87 | but using a socket on the SOCKS server rather than on the client.
88 |
89 | The client includes in the request packet the IP address of the
90 | application server, the destination port used in the primary connection,
91 | and the userid.
92 |
93 | +----+----+----+----+----+----+----+----+----+----+....+----+
94 | | VN | CD | DSTPORT | DSTIP | USERID |NULL|
95 | +----+----+----+----+----+----+----+----+----+----+....+----+
96 | # of bytes: 1 1 2 4 variable 1
97 |
98 | VN is again 4 for the SOCKS protocol version number. CD must be 2 to
99 | indicate BIND request.
100 |
101 | The SOCKS server uses the client information to decide whether the
102 | request is to be granted. The reply it sends back to the client has
103 | the same format as the reply for CONNECT request, i.e.,
104 |
105 | +----+----+----+----+----+----+----+----+
106 | | VN | CD | DSTPORT | DSTIP |
107 | +----+----+----+----+----+----+----+----+
108 | # of bytes: 1 1 2 4
109 |
110 | VN is the version of the reply code and should be 0. CD is the result
111 | code with one of the following values:
112 |
113 | 90: request granted
114 | 91: request rejected or failed
115 | 92: request rejected becasue SOCKS server cannot connect to
116 | identd on the client
117 | 93: request rejected because the client program and identd
118 | report different user-ids.
119 |
120 | However, for a granted request (CD is 90), the DSTPORT and DSTIP fields
121 | are meaningful. In that case, the SOCKS server obtains a socket to wait
122 | for an incoming connection and sends the port number and the IP address
123 | of that socket to the client in DSTPORT and DSTIP, respectively. If the
124 | DSTIP in the reply is 0 (the value of constant INADDR_ANY), then the
125 | client should replace it by the IP address of the SOCKS server to which
126 | the cleint is connected. (This happens if the SOCKS server is not a
127 | multi-homed host.) In the typical scenario, these two numbers are
128 | made available to the application client prgram via the result of the
129 | subsequent getsockname() call. The application protocol must provide a
130 | way for these two pieces of information to be sent from the client to
131 | the application server so that it can initiate the connection, which
132 | connects it to the SOCKS server rather than directly to the application
133 | client as it normally would.
134 |
135 | The SOCKS server sends a second reply packet to the client when the
136 | anticipated connection from the application server is established.
137 | The SOCKS server checks the IP address of the originating host against
138 | the value of DSTIP specified in the client's BIND request. If a mismatch
139 | is found, the CD field in the second reply is set to 91 and the SOCKS
140 | server closes both connections. If the two match, CD in the second
141 | reply is set to 90 and the SOCKS server gets ready to relay the traffic
142 | on its two connections. From then on the client does I/O on its connection
143 | to the SOCKS server as if it were directly connected to the application
144 | server.
145 |
146 |
147 |
148 | For both CONNECT and BIND operations, the server sets a time limit
149 | (2 minutes in current CSTC implementation) for the establishment of its
150 | connection with the application server. If the connection is still not
151 | establiched when the time limit expires, the server closes its connection
152 | to the client and gives up.
153 |
--------------------------------------------------------------------------------
/doc/socks4a.protocol.txt:
--------------------------------------------------------------------------------
1 | SOCKS 4A: A Simple Extension to SOCKS 4 Protocol
2 |
3 | Ying-Da Lee
4 | yingda@best.com or yingda@esd.sgi.com
5 |
6 | Please read SOCKS4.protocol first for an description of the version 4
7 | protocol. This extension is intended to allow the use of SOCKS on hosts
8 | which are not capable of resolving all domain names.
9 |
10 | In version 4, the client sends the following packet to the SOCKS server
11 | to request a CONNECT or a BIND operation:
12 |
13 | +----+----+----+----+----+----+----+----+----+----+....+----+
14 | | VN | CD | DSTPORT | DSTIP | USERID |NULL|
15 | +----+----+----+----+----+----+----+----+----+----+....+----+
16 | # of bytes: 1 1 2 4 variable 1
17 |
18 | VN is the SOCKS protocol version number and should be 4. CD is the
19 | SOCKS command code and should be 1 for CONNECT or 2 for BIND. NULL
20 | is a byte of all zero bits.
21 |
22 | For version 4A, if the client cannot resolve the destination host's
23 | domain name to find its IP address, it should set the first three bytes
24 | of DSTIP to NULL and the last byte to a non-zero value. (This corresponds
25 | to IP address 0.0.0.x, with x nonzero. As decreed by IANA -- The
26 | Internet Assigned Numbers Authority -- such an address is inadmissible
27 | as a destination IP address and thus should never occur if the client
28 | can resolve the domain name.) Following the NULL byte terminating
29 | USERID, the client must sends the destination domain name and termiantes
30 | it with another NULL byte. This is used for both CONNECT and BIND requests.
31 |
32 | A server using protocol 4A must check the DSTIP in the request packet.
33 | If it represent address 0.0.0.x with nonzero x, the server must read
34 | in the domain name that the client sends in the packet. The server
35 | should resolve the domain name and make connection to the destination
36 | host if it can.
37 |
38 | SOCKSified sockd may pass domain names that it cannot resolve to
39 | the next-hop SOCKS server.
40 |
--------------------------------------------------------------------------------
/http-auth.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | *
16 | *
17 | * http-auth library, provide basic and digest scheme
18 | * see RFC 2617 for details
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "md5.h"
27 | #include "base64.h"
28 | #include "log.h"
29 | #include "http-auth.h"
30 |
31 | char* basic_authentication_encode(const char *user, const char *passwd)
32 | {
33 | /* prepare the user:pass key pair */
34 | int pair_len = strlen(user) + 1 + strlen(passwd);
35 | char pair[pair_len + 1];
36 |
37 | sprintf(pair, "%s:%s", user, passwd);
38 |
39 | /* calculate the final string length */
40 | int basic_len = BASE64_SIZE(pair_len);
41 | char *basic_ptr = calloc(basic_len + 1, 1);
42 |
43 | if (!base64_encode(basic_ptr, basic_len, (const uint8_t*)pair, pair_len))
44 | return NULL;
45 |
46 | return basic_ptr;
47 | }
48 |
49 | #define MD5_HASHLEN (16)
50 |
51 | static void dump_hash(char *buf, const unsigned char *hash)
52 | {
53 | int i;
54 |
55 | for (i = 0; i < MD5_HASHLEN; i++) {
56 | buf += sprintf(buf, "%02x", hash[i]);
57 | }
58 |
59 | *buf = 0;
60 | }
61 |
62 | typedef struct {
63 | const char *b, *e;
64 | } param_token;
65 |
66 | /* Extract a parameter from the string (typically an HTTP header) at
67 | **SOURCE and advance SOURCE to the next parameter. Return false
68 | when there are no more parameters to extract. The name of the
69 | parameter is returned in NAME, and the value in VALUE. If the
70 | parameter has no value, the token's b==e.*/
71 |
72 | static int extract_param(const char **source, param_token *name, param_token *value, char separator)
73 | {
74 | const char *p = *source;
75 |
76 | while (isspace (*p))
77 | ++p;
78 |
79 | if (!*p)
80 | {
81 | *source = p;
82 | return 0; /* no error; nothing more to extract */
83 | }
84 |
85 | /* Extract name. */
86 | name->b = p;
87 | while (*p && !isspace (*p) && *p != '=' && *p != separator)
88 | ++p;
89 | name->e = p;
90 | if (name->b == name->e)
91 | return 0; /* empty name: error */
92 |
93 | while (isspace (*p))
94 | ++p;
95 | if (*p == separator || !*p) /* no value */
96 | {
97 | value->b = value->e = "";
98 | if (*p == separator) ++p;
99 | *source = p;
100 | return 1;
101 | }
102 | if (*p != '=')
103 | return 0; /* error */
104 |
105 | /* *p is '=', extract value */
106 | ++p;
107 | while (isspace (*p)) ++p;
108 | if (*p == '"') /* quoted */
109 | {
110 | value->b = ++p;
111 | while (*p && *p != '"') ++p;
112 | if (!*p)
113 | return 0;
114 | value->e = p++;
115 | /* Currently at closing quote; find the end of param. */
116 | while (isspace (*p)) ++p;
117 | while (*p && *p != separator) ++p;
118 | if (*p == separator)
119 | ++p;
120 | else if (*p)
121 | /* garbage after closed quote, e.g. foo="bar"baz */
122 | return 0;
123 | }
124 | else /* unquoted */
125 | {
126 | value->b = p;
127 | while (*p && *p != separator) ++p;
128 | value->e = p;
129 | while (value->e != value->b && isspace (value->e[-1]))
130 | --value->e;
131 | if (*p == separator) ++p;
132 | }
133 | *source = p;
134 | return 1;
135 |
136 | }
137 |
138 | char* digest_authentication_encode(const char *line, const char *user, const char *passwd,
139 | const char *method, const char *path, int count, const char *cnonce)
140 | {
141 | char *realm = NULL, *opaque = NULL, *nonce = NULL, *qop = NULL;
142 | char nc[9];
143 | sprintf(nc, "%08x", count);
144 |
145 | const char *ptr = line;
146 | param_token name, value;
147 |
148 | while (extract_param(&ptr, &name, &value, ',')) {
149 | int namelen = name.e - name.b;
150 | int valuelen = value.e - value.b;
151 |
152 | if (strncasecmp(name.b, "realm" , namelen) == 0) {
153 | strncpy(realm = calloc(valuelen + 1, 1), value.b, valuelen);
154 | realm[valuelen] = '\0';
155 | }
156 | else if (strncasecmp(name.b, "opaque", namelen) == 0) {
157 | strncpy(opaque = calloc(valuelen + 1, 1), value.b, valuelen);
158 | opaque[valuelen] = '\0';
159 | }
160 | else if (strncasecmp(name.b, "nonce" , namelen) == 0) {
161 | strncpy(nonce = calloc(valuelen + 1, 1), value.b, valuelen);
162 | nonce[valuelen] = '\0';
163 | }
164 | else if (strncasecmp(name.b, "qop" , namelen) == 0) {
165 | strncpy(qop = calloc(valuelen + 1, 1), value.b, valuelen);
166 | qop[valuelen] = '\0';
167 | }
168 | }
169 |
170 | if (!realm || !nonce || !user || !passwd || !path || !method) {
171 | free(realm);
172 | free(opaque);
173 | free(nonce);
174 | free(qop);
175 | return NULL;
176 | }
177 |
178 | if (qop && strncasecmp(qop, "auth", 5) != 0) {
179 | /* FIXME: currently don't support auth-int, only "auth" is supported */
180 | free(realm);
181 | free(opaque);
182 | free(nonce);
183 | free(qop);
184 | return NULL;
185 | }
186 |
187 | /* calculate the digest value */
188 | md5_state_t ctx;
189 | md5_byte_t hash[MD5_HASHLEN];
190 | char a1buf[MD5_HASHLEN * 2 + 1], a2buf[MD5_HASHLEN * 2 + 1];
191 | char response[MD5_HASHLEN * 2 + 1];
192 |
193 | /* A1 = username-value ":" realm-value ":" passwd */
194 | md5_init(&ctx);
195 | md5_append(&ctx, (md5_byte_t*)user, strlen(user));
196 | md5_append(&ctx, (md5_byte_t*)":", 1);
197 | md5_append(&ctx, (md5_byte_t*)realm, strlen(realm));
198 | md5_append(&ctx, (md5_byte_t*)":", 1);
199 | md5_append(&ctx, (md5_byte_t*)passwd, strlen(passwd));
200 | md5_finish(&ctx, hash);
201 | dump_hash(a1buf, hash);
202 |
203 | /* A2 = Method ":" digest-uri-value */
204 | md5_init(&ctx);
205 | md5_append(&ctx, (md5_byte_t*)method, strlen(method));
206 | md5_append(&ctx, (md5_byte_t*)":", 1);
207 | md5_append(&ctx, (md5_byte_t*)path, strlen(path));
208 | md5_finish(&ctx, hash);
209 | dump_hash(a2buf, hash);
210 |
211 | /* qop set: request-digest = H(A1) ":" nonce-value ":" nc-value ":" cnonce-value ":" qop-value ":" H(A2) */
212 | /* not set: request-digest = H(A1) ":" nonce-value ":" H(A2) */
213 | md5_init(&ctx);
214 | md5_append(&ctx, (md5_byte_t*)a1buf, strlen(a1buf));
215 | md5_append(&ctx, (md5_byte_t*)":", 1);
216 | md5_append(&ctx, (md5_byte_t*)nonce, strlen(nonce));
217 | md5_append(&ctx, (md5_byte_t*)":", 1);
218 | if (qop) {
219 | md5_append(&ctx, (md5_byte_t*)nc, strlen(nc));
220 | md5_append(&ctx, (md5_byte_t*)":", 1);
221 | md5_append(&ctx, (md5_byte_t*)cnonce, strlen(cnonce));
222 | md5_append(&ctx, (md5_byte_t*)":", 1);
223 | md5_append(&ctx, (md5_byte_t*)qop, strlen(qop));
224 | md5_append(&ctx, (md5_byte_t*)":", 1);
225 | }
226 | md5_append(&ctx, (md5_byte_t*)a2buf, strlen(a2buf));
227 | md5_finish(&ctx, hash);
228 | dump_hash(response, hash);
229 |
230 | /* prepare the final string */
231 | int len = 256;
232 | len += strlen(user);
233 | len += strlen(realm);
234 | len += strlen(nonce);
235 | len += strlen(path);
236 | len += strlen(response);
237 |
238 | if (qop) {
239 | len += strlen(qop);
240 | len += strlen(nc);
241 | len += strlen(cnonce);
242 | }
243 |
244 | if (opaque) {
245 | len += strlen(opaque);
246 | }
247 |
248 | char *res = (char*)malloc(len);
249 | if (!qop) {
250 | sprintf(res, "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
251 | user, realm, nonce, path, response);
252 | } else {
253 | sprintf(res, "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", qop=%s, nc=%s, cnonce=\"%s\"",
254 | user, realm, nonce, path, response, qop, nc, cnonce);
255 | }
256 |
257 | if (opaque) {
258 | char *p = res + strlen(res);
259 | strcat (p, ", opaque=\"");
260 | strcat (p, opaque);
261 | strcat (p, "\"");
262 | }
263 |
264 | free(realm);
265 | free(opaque);
266 | free(nonce);
267 | free(qop);
268 | return res;
269 | }
270 |
271 | const char *auth_request_header = "Proxy-Authenticate:";
272 | const char *auth_response_header = "Proxy-Authorization:";
273 |
274 | char *http_auth_request_header(struct evbuffer *src, struct evbuffer *tee)
275 | {
276 | char *line;
277 | for (;;) {
278 | line = redsocks_evbuffer_readline(src);
279 | if (tee && line) {
280 | if (evbuffer_add(tee, line, strlen(line)) != 0 ||
281 | evbuffer_add(tee, "\r\n", 2) != 0)
282 | {
283 | log_error(LOG_NOTICE, "evbuffer_add");
284 | free(line);
285 | return NULL; // I'm going up straight to the 403...
286 | }
287 | }
288 | // FIXME: multi-line headers are not supported
289 | if (line == NULL || *line == '\0' || strchr(line, ':') == NULL) {
290 | free(line);
291 | return NULL;
292 | }
293 | if (strncasecmp(line, auth_request_header, strlen(auth_request_header)) == 0)
294 | return line;
295 | free(line);
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/http-auth.h:
--------------------------------------------------------------------------------
1 | #ifndef HTTP_AUTH_H
2 | #define HTTP_AUTH_H
3 |
4 | #include "redsocks.h"
5 |
6 | typedef struct http_auth_t {
7 | char *last_auth_query;
8 | int last_auth_count;
9 | } http_auth;
10 |
11 | static inline http_auth* red_http_auth(redsocks_instance *i)
12 | {
13 | return (http_auth*)(i + 1);
14 | }
15 |
16 | /*
17 | * Create the authentication header contents for the `Basic' scheme.
18 | * This is done by encoding the string "USER:PASS" to base64 and
19 | * prepending the string "Basic " in front of it.
20 | *
21 | */
22 |
23 | char* basic_authentication_encode(const char *user, const char *passwd);
24 |
25 | /*
26 | * Create the authentication header contents for the 'Digest' scheme.
27 | * only md5 method is available, see RFC 2617 for detail.
28 | *
29 | */
30 | char* digest_authentication_encode(const char *line, const char *user, const char *passwd,
31 | const char *method, const char *path, int count, const char *cnonce);
32 |
33 | char *http_auth_request_header(struct evbuffer *src, struct evbuffer *tee);
34 |
35 | #endif /* HTTP_AUTH_H */
36 |
--------------------------------------------------------------------------------
/http-connect.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | *
16 | *
17 | * http-connect upstream module for redsocks
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "log.h"
31 | #include "redsocks.h"
32 | #include "http-auth.h"
33 |
34 | typedef enum httpc_state_t {
35 | httpc_new,
36 | httpc_request_sent,
37 | httpc_reply_came, // 200 OK came, skipping headers...
38 | httpc_headers_skipped, // starting pump!
39 | httpc_no_way, // proxy can't handle the request
40 | httpc_MAX,
41 | } httpc_state;
42 |
43 |
44 | #define HTTP_HEAD_WM_HIGH 4096 // that should be enough for one HTTP line.
45 |
46 |
47 | static void httpc_client_init(redsocks_client *client)
48 | {
49 | client->state = httpc_new;
50 | }
51 |
52 | static void httpc_instance_fini(redsocks_instance *instance)
53 | {
54 | http_auth *auth = red_http_auth(instance);
55 | free(auth->last_auth_query);
56 | auth->last_auth_query = NULL;
57 | }
58 |
59 | static struct evbuffer *httpc_mkconnect(redsocks_client *client);
60 |
61 | extern const char *auth_request_header;
62 | extern const char *auth_response_header;
63 |
64 | static void httpc_read_cb(struct bufferevent *buffev, void *_arg)
65 | {
66 | redsocks_client *client = _arg;
67 |
68 | assert(client->relay == buffev);
69 | assert(client->state == httpc_request_sent || client->state == httpc_reply_came);
70 |
71 | redsocks_touch_client(client);
72 |
73 | // evbuffer_add() triggers callbacks, so we can't write to client->client
74 | // till we know that we're going to ONFAIL_FORWARD_HTTP_ERR.
75 | // And the decision is made when all the headers are processed.
76 | struct evbuffer* tee = NULL;
77 | const bool do_errtee = client->instance->config.on_proxy_fail == ONFAIL_FORWARD_HTTP_ERR;
78 |
79 | if (client->state == httpc_request_sent) {
80 | size_t len = evbuffer_get_length(buffev->input);
81 | char *line = redsocks_evbuffer_readline(buffev->input);
82 | if (line) {
83 | unsigned int code;
84 | if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match
85 | if (code == 407) { // auth failed
86 | http_auth *auth = red_http_auth(client->instance);
87 |
88 | if (auth->last_auth_query != NULL && auth->last_auth_count == 1) {
89 | redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth failed: %s", line);
90 | client->state = httpc_no_way;
91 | } else if (client->instance->config.login == NULL || client->instance->config.password == NULL) {
92 | redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no login/password configured: %s", line);
93 | client->state = httpc_no_way;
94 | } else {
95 | if (do_errtee)
96 | tee = evbuffer_new();
97 | char *auth_request = http_auth_request_header(buffev->input, tee);
98 | if (!auth_request) {
99 | redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no <%s> header found: %s", auth_request_header, line);
100 | client->state = httpc_no_way;
101 | } else {
102 | free(line);
103 | if (tee)
104 | evbuffer_free(tee);
105 | free(auth->last_auth_query);
106 | char *ptr = auth_request;
107 |
108 | ptr += strlen(auth_request_header);
109 | while (isspace(*ptr))
110 | ptr++;
111 |
112 | size_t last_auth_query_len = strlen(ptr) + 1;
113 | auth->last_auth_query = calloc(last_auth_query_len, 1);
114 | memcpy(auth->last_auth_query, ptr, last_auth_query_len);
115 | auth->last_auth_count = 0;
116 |
117 | free(auth_request);
118 |
119 | if (bufferevent_disable(client->relay, EV_WRITE)) {
120 | redsocks_log_errno(client, LOG_ERR, "bufferevent_disable");
121 | return;
122 | }
123 |
124 | /* close relay tunnel */
125 | redsocks_bufferevent_free(client->relay);
126 |
127 | /* set to initial state*/
128 | client->state = httpc_new;
129 |
130 | /* and reconnect */
131 | redsocks_connect_relay(client);
132 | return;
133 | }
134 | }
135 | } else if (200 <= code && code <= 299) {
136 | client->state = httpc_reply_came;
137 | } else {
138 | redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy error: %s", line);
139 | client->state = httpc_no_way;
140 | }
141 | } else {
142 | redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy bad firstline: %s", line);
143 | client->state = httpc_no_way;
144 | }
145 | if (do_errtee && client->state == httpc_no_way) {
146 | if (bufferevent_write(client->client, line, strlen(line)) != 0 ||
147 | bufferevent_write(client->client, "\r\n", 2) != 0)
148 | {
149 | redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write");
150 | goto fail;
151 | }
152 | }
153 | free(line);
154 | }
155 | else if (len >= HTTP_HEAD_WM_HIGH) {
156 | redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy reply is too long, %zu bytes", len);
157 | client->state = httpc_no_way;
158 | }
159 | }
160 |
161 | if (do_errtee && client->state == httpc_no_way) {
162 | if (tee) {
163 | if (bufferevent_write_buffer(client->client, tee) != 0) {
164 | redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write_buffer");
165 | goto fail;
166 | }
167 | }
168 | redsocks_shutdown(client, client->client, SHUT_RD);
169 | const size_t avail = evbuffer_get_length(client->client->input);
170 | if (avail) {
171 | if (evbuffer_drain(client->client->input, avail) != 0) {
172 | redsocks_log_errno(client, LOG_NOTICE, "evbuffer_drain");
173 | goto fail;
174 | }
175 | }
176 | redsocks_shutdown(client, client->relay, SHUT_WR);
177 | client->state = httpc_headers_skipped;
178 | }
179 |
180 | fail:
181 | if (tee) {
182 | evbuffer_free(tee);
183 | }
184 |
185 | if (client->state == httpc_no_way) {
186 | redsocks_drop_client(client);
187 | return;
188 | }
189 |
190 | while (client->state == httpc_reply_came) {
191 | char *line = redsocks_evbuffer_readline(buffev->input);
192 | if (line) {
193 | if (strlen(line) == 0) {
194 | client->state = httpc_headers_skipped;
195 | }
196 | free(line);
197 | }
198 | else {
199 | break;
200 | }
201 | }
202 |
203 | if (client->state == httpc_headers_skipped) {
204 | redsocks_start_relay(client);
205 | }
206 | }
207 |
208 | static struct evbuffer *httpc_mkconnect(redsocks_client *client)
209 | {
210 | struct evbuffer *buff = NULL, *retval = NULL;
211 | char *auth_string = NULL;
212 | int len;
213 |
214 | buff = evbuffer_new();
215 | if (!buff) {
216 | redsocks_log_errno(client, LOG_ERR, "evbuffer_new");
217 | goto fail;
218 | }
219 |
220 | http_auth *auth = red_http_auth(client->instance);
221 | ++auth->last_auth_count;
222 |
223 | const char *auth_scheme = NULL;
224 |
225 | if (auth->last_auth_query != NULL) {
226 | /* find previous auth challange */
227 |
228 | if (strncasecmp(auth->last_auth_query, "Basic", 5) == 0) {
229 | auth_string = basic_authentication_encode(client->instance->config.login, client->instance->config.password);
230 | auth_scheme = "Basic";
231 | } else if (strncasecmp(auth->last_auth_query, "Digest", 6) == 0) {
232 | /* calculate uri */
233 | char uri[128];
234 | snprintf(uri, 128, "%s:%u", inet_ntoa(client->destaddr.sin_addr), ntohs(client->destaddr.sin_port));
235 |
236 | /* prepare an random string for cnounce */
237 | char cnounce[17];
238 | snprintf(cnounce, sizeof(cnounce), "%08x%08x", red_randui32(), red_randui32());
239 |
240 | auth_string = digest_authentication_encode(auth->last_auth_query + 7, //line
241 | client->instance->config.login, client->instance->config.password, //user, pass
242 | "CONNECT", uri, auth->last_auth_count, cnounce); // method, path, nc, cnounce
243 | auth_scheme = "Digest";
244 | }
245 | }
246 |
247 | // TODO: do accurate evbuffer_expand() while cleaning up http-auth
248 | len = evbuffer_add_printf(buff, "CONNECT %s:%u HTTP/1.0\r\n",
249 | inet_ntoa(client->destaddr.sin_addr),
250 | ntohs(client->destaddr.sin_port));
251 | if (len < 0) {
252 | redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf");
253 | goto fail;
254 | }
255 |
256 | if (auth_string) {
257 | len = evbuffer_add_printf(buff, "%s %s %s\r\n",
258 | auth_response_header, auth_scheme, auth_string);
259 | if (len < 0) {
260 | redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf");
261 | goto fail;
262 | }
263 | free(auth_string);
264 | auth_string = NULL;
265 | }
266 |
267 | const enum disclose_src_e disclose_src = client->instance->config.disclose_src;
268 | if (disclose_src != DISCLOSE_NONE) {
269 | char clientip[INET_ADDRSTRLEN];
270 | const char *ip = inet_ntop(client->clientaddr.sin_family, &client->clientaddr.sin_addr, clientip, sizeof(clientip));
271 | if (!ip) {
272 | redsocks_log_errno(client, LOG_ERR, "inet_ntop");
273 | goto fail;
274 | }
275 | if (disclose_src == DISCLOSE_X_FORWARDED_FOR) {
276 | len = evbuffer_add_printf(buff, "X-Forwarded-For: %s\r\n", ip);
277 | } else if (disclose_src == DISCLOSE_FORWARDED_IP) {
278 | len = evbuffer_add_printf(buff, "Forwarded: for=%s\r\n", ip);
279 | } else if (disclose_src == DISCLOSE_FORWARDED_IPPORT) {
280 | len = evbuffer_add_printf(buff, "Forwarded: for=\"%s:%d\"\r\n", ip,
281 | ntohs(client->clientaddr.sin_port));
282 | }
283 | if (len < 0) {
284 | redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf");
285 | goto fail;
286 | }
287 | }
288 |
289 | len = evbuffer_add(buff, "\r\n", 2);
290 | if (len < 0) {
291 | redsocks_log_errno(client, LOG_ERR, "evbufer_add");
292 | goto fail;
293 | }
294 |
295 | retval = buff;
296 | buff = NULL;
297 |
298 | fail:
299 | if (auth_string)
300 | free(auth_string);
301 | if (buff)
302 | evbuffer_free(buff);
303 | return retval;
304 | }
305 |
306 |
307 | static void httpc_write_cb(struct bufferevent *buffev, void *_arg)
308 | {
309 | redsocks_client *client = _arg;
310 |
311 | redsocks_touch_client(client);
312 |
313 | if (client->state == httpc_new) {
314 | redsocks_write_helper_ex(
315 | buffev, client,
316 | httpc_mkconnect, httpc_request_sent, 1, HTTP_HEAD_WM_HIGH
317 | );
318 | }
319 | else if (client->state >= httpc_request_sent) {
320 | bufferevent_disable(buffev, EV_WRITE);
321 | }
322 | }
323 |
324 |
325 | relay_subsys http_connect_subsys =
326 | {
327 | .name = "http-connect",
328 | .payload_len = 0,
329 | .instance_payload_len = sizeof(http_auth),
330 | .readcb = httpc_read_cb,
331 | .writecb = httpc_write_cb,
332 | .init = httpc_client_init,
333 | .instance_fini = httpc_instance_fini,
334 | };
335 |
336 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
337 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
338 |
--------------------------------------------------------------------------------
/libc-compat.h:
--------------------------------------------------------------------------------
1 | #ifndef UUID_67C91670_FCCB_4855_BDF7_609F1EECB8B4
2 | #define UUID_67C91670_FCCB_4855_BDF7_609F1EECB8B4
3 |
4 | /* all these definitions, are included into bits/in.h from libc6-dev 2.15-0ubuntu10
5 | * from Ubuntu 12.04 and is not included into libc6-dev 2.11.1-0ubuntu7.10 from
6 | * Ubuntu 10.04.
7 | * linux/in.h is not included directly because of lots of redefinitions,
8 | * extracting single value from linux/in.h is not done because it looks like
9 | * autotools reinvention */
10 | #ifndef IP_ORIGDSTADDR
11 | # warning Using hardcoded value for IP_ORIGDSTADDR as libc headers do not define it.
12 | # define IP_ORIGDSTADDR 20
13 | #endif
14 |
15 | #ifndef IP_RECVORIGDSTADDR
16 | # warning Using hardcoded value for IP_RECVORIGDSTADDR as libc headers do not define it.
17 | # define IP_RECVORIGDSTADDR IP_ORIGDSTADDR
18 | #endif
19 |
20 | #ifndef IP_TRANSPARENT
21 | # warning Using hardcoded value for IP_TRANSPARENT as libc headers do not define it.
22 | # define IP_TRANSPARENT 19
23 | #endif
24 |
25 | #endif // 67C91670_FCCB_4855_BDF7_609F1EECB8B4
26 |
--------------------------------------------------------------------------------
/libevent-compat.h:
--------------------------------------------------------------------------------
1 | #ifndef UUID_FC148CFA_5ECC_488E_8A62_CD39406C9F1E
2 | #define UUID_FC148CFA_5ECC_488E_8A62_CD39406C9F1E
3 |
4 | /* evutil_socket_t is macros in libevent-2.0, not typedef, libevent-1.4 is
5 | * still supported because of Ubuntu 10.04 LTS */
6 | #ifndef evutil_socket_t
7 | # warning Using hardcoded value for evutil_socket_t as libevent headers do not define it.
8 | # define evutil_socket_t int
9 | #endif
10 |
11 | #endif // FC148CFA_5ECC_488E_8A62_CD39406C9F1E
12 |
--------------------------------------------------------------------------------
/log.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include "utils.h"
24 | #include "log.h"
25 |
26 | const char *error_lowmem = "";
27 |
28 | typedef void (*log_func)(const char *file, int line, const char *func, int priority, const char *message, const char *appendix);
29 |
30 | static const char* getprioname(int priority)
31 | {
32 | switch (priority) {
33 | case LOG_EMERG: return "emerg";
34 | case LOG_ALERT: return "alert";
35 | case LOG_CRIT: return "crit";
36 | case LOG_ERR: return "err";
37 | case LOG_WARNING: return "warning";
38 | case LOG_NOTICE: return "notice";
39 | case LOG_INFO: return "info";
40 | case LOG_DEBUG: return "debug";
41 | default: return "?";
42 | }
43 | }
44 |
45 | static void fprint_timestamp(
46 | FILE* fd,
47 | const char *file, int line, const char *func, int priority, const char *message, const char *appendix)
48 | {
49 | struct timeval tv = { };
50 | gettimeofday(&tv, 0);
51 |
52 | /* XXX: there is no error-checking, IMHO it's better to lose messages
53 | * then to die and stop service */
54 | const char* sprio = getprioname(priority);
55 | if (appendix)
56 | fprintf(fd, "%lu.%6.6lu %s %s:%u %s(...) %s: %s\n", tv.tv_sec, tv.tv_usec, sprio, file, line, func, message, appendix);
57 | else
58 | fprintf(fd, "%lu.%6.6lu %s %s:%u %s(...) %s\n", tv.tv_sec, tv.tv_usec, sprio, file, line, func, message);
59 | }
60 |
61 | static void stderr_msg(const char *file, int line, const char *func, int priority, const char *message, const char *appendix)
62 | {
63 | fprint_timestamp(stderr, file, line, func, priority, message, appendix);
64 | }
65 |
66 | static FILE *logfile = NULL;
67 |
68 | static void logfile_msg(const char *file, int line, const char *func, int priority, const char *message, const char *appendix)
69 | {
70 | fprint_timestamp(logfile, file, line, func, priority, message, appendix);
71 | fflush(logfile);
72 | }
73 |
74 | static void syslog_msg(const char *file, int line, const char *func, int priority, const char *message, const char *appendix)
75 | {
76 | if (appendix)
77 | syslog(priority, "%s: %s\n", message, appendix);
78 | else
79 | syslog(priority, "%s\n", message);
80 | }
81 |
82 | static log_func log_msg = stderr_msg;
83 | static log_func log_msg_next = NULL;
84 | static bool should_log_info = true;
85 | static bool should_log_debug = false;
86 |
87 | bool should_log(int priority)
88 | {
89 | return (priority != LOG_DEBUG && priority != LOG_INFO)
90 | || (priority == LOG_DEBUG && should_log_debug)
91 | || (priority == LOG_INFO && should_log_info);
92 | }
93 |
94 | int log_preopen(const char *dst, bool log_debug, bool log_info)
95 | {
96 | const char *syslog_prefix = "syslog:";
97 | const char *file_prefix = "file:";
98 | should_log_debug = log_debug;
99 | should_log_info = log_info;
100 | if (strcmp(dst, "stderr") == 0) {
101 | log_msg_next = stderr_msg;
102 | }
103 | else if (strncmp(dst, syslog_prefix, strlen(syslog_prefix)) == 0) {
104 | const char *facility_name = dst + strlen(syslog_prefix);
105 | int facility = -1;
106 | int logmask;
107 | struct {
108 | char *name; int value;
109 | } *ptpl, tpl[] = {
110 | { "daemon", LOG_DAEMON },
111 | { "local0", LOG_LOCAL0 },
112 | { "local1", LOG_LOCAL1 },
113 | { "local2", LOG_LOCAL2 },
114 | { "local3", LOG_LOCAL3 },
115 | { "local4", LOG_LOCAL4 },
116 | { "local5", LOG_LOCAL5 },
117 | { "local6", LOG_LOCAL6 },
118 | { "local7", LOG_LOCAL7 },
119 | };
120 |
121 | FOREACH(ptpl, tpl)
122 | if (strcmp(facility_name, ptpl->name) == 0) {
123 | facility = ptpl->value;
124 | break;
125 | }
126 | if (facility == -1) {
127 | log_error(LOG_ERR, "log_preopen(%s, ...): unknown syslog facility", dst);
128 | return -1;
129 | }
130 |
131 | openlog("redsocks", LOG_NDELAY | LOG_PID, facility);
132 |
133 | logmask = setlogmask(0);
134 | if (!log_debug)
135 | logmask &= ~(LOG_MASK(LOG_DEBUG));
136 | if (!log_info)
137 | logmask &= ~(LOG_MASK(LOG_INFO));
138 | setlogmask(logmask);
139 |
140 | log_msg_next = syslog_msg;
141 | }
142 | else if (strncmp(dst, file_prefix, strlen(file_prefix)) == 0) {
143 | const char *filename = dst + strlen(file_prefix);
144 | if ((logfile = fopen(filename, "a")) == NULL) {
145 | log_error(LOG_ERR, "log_preopen(%s, ...): %s", dst, strerror(errno));
146 | return -1;
147 | }
148 | log_msg_next = logfile_msg;
149 | /* TODO: add log rotation */
150 | }
151 | else {
152 | log_error(LOG_ERR, "log_preopen(%s, ...): unknown destination", dst);
153 | return -1;
154 | }
155 | return 0;
156 | }
157 |
158 | void log_open()
159 | {
160 | log_msg = log_msg_next;
161 | log_msg_next = NULL;
162 | }
163 |
164 | void _log_vwrite(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, va_list ap)
165 | {
166 | if (!should_log(priority))
167 | return;
168 |
169 | int saved_errno = errno;
170 | struct evbuffer *buff = evbuffer_new();
171 | const char *message;
172 |
173 | if (buff) {
174 | evbuffer_add_vprintf(buff, fmt, ap);
175 | message = (const char*)evbuffer_pullup(buff, -1);
176 | }
177 | else
178 | message = error_lowmem;
179 |
180 | log_msg(file, line, func, priority, message, do_errno ? strerror(saved_errno) : NULL);
181 |
182 | if (buff)
183 | evbuffer_free(buff);
184 | }
185 |
186 | void _log_write(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, ...)
187 | {
188 | if (!should_log(priority))
189 | return;
190 |
191 | va_list ap;
192 |
193 | va_start(ap, fmt);
194 | _log_vwrite(file, line, func, do_errno, priority, fmt, ap);
195 | va_end(ap);
196 | }
197 |
198 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
199 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
200 |
--------------------------------------------------------------------------------
/log.h:
--------------------------------------------------------------------------------
1 | #ifndef LOG_H_WED_JAN_24_18_21_27_2007
2 | #define LOG_H_WED_JAN_24_18_21_27_2007
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #define log_errno(prio, msg...) _log_write(__FILE__, __LINE__, __func__, 1, prio, ## msg)
9 | #define log_error(prio, msg...) _log_write(__FILE__, __LINE__, __func__, 0, prio, ## msg)
10 |
11 | extern const char *error_lowmem;
12 |
13 | int log_preopen(const char *dst, bool log_debug, bool log_info);
14 | void log_open();
15 |
16 | bool should_log(int priority);
17 |
18 | void _log_vwrite(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, va_list ap);
19 |
20 | void _log_write(const char *file, int line, const char *func, int do_errno, int priority, const char *fmt, ...)
21 | #if defined(__GNUC__)
22 | __attribute__ (( format (printf, 6, 7) ))
23 | #endif
24 | ;
25 |
26 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
27 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
28 | #endif /* LOG_H_WED_JAN_24_18_21_27_2007 */
29 |
30 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include "log.h"
26 | #include "main.h"
27 | #include "utils.h"
28 | #include "version.h"
29 | #include "config.h"
30 | #include "base.h"
31 |
32 | extern app_subsys redsocks_subsys;
33 | extern app_subsys debug_subsys;
34 | extern app_subsys base_subsys;
35 | extern app_subsys redudp_subsys;
36 | extern app_subsys dnstc_subsys;
37 | extern app_subsys dnsu2t_subsys;
38 |
39 | app_subsys *subsystems[] = {
40 | &redsocks_subsys,
41 | #ifdef DBG_BUILD
42 | &debug_subsys,
43 | #endif
44 | &base_subsys,
45 | &redudp_subsys,
46 | &dnstc_subsys,
47 | &dnsu2t_subsys,
48 | };
49 |
50 | static const char *confname = "redsocks.conf";
51 | static const char *pidfile = NULL;
52 |
53 | static void terminate(int sig, short what, void *_arg)
54 | {
55 | if (event_loopbreak() != 0)
56 | log_error(LOG_WARNING, "event_loopbreak");
57 | }
58 |
59 | int main(int argc, char **argv)
60 | {
61 | int error;
62 | app_subsys **ss;
63 | int exit_signals[2] = {SIGTERM, SIGINT};
64 | struct event terminators[2];
65 | bool conftest = false;
66 | int opt;
67 | int i;
68 |
69 | evutil_secure_rng_init();
70 | while ((opt = getopt(argc, argv, "h?vtc:p:")) != -1) {
71 | switch (opt) {
72 | case 't':
73 | conftest = true;
74 | break;
75 | case 'c':
76 | confname = optarg;
77 | break;
78 | case 'p':
79 | pidfile = optarg;
80 | break;
81 | case 'v':
82 | puts(redsocks_version);
83 | printf("Built with libevent-%s\n", LIBEVENT_VERSION);
84 | printf("Runs with libevent-%s\n", event_get_version());
85 | if (LIBEVENT_VERSION_NUMBER != event_get_version_number()) {
86 | printf("Warning: libevent version number mismatch.\n"
87 | " Headers: %8x\n"
88 | " Runtime: %8x\n", LIBEVENT_VERSION_NUMBER, event_get_version_number());
89 | }
90 | return EXIT_SUCCESS;
91 | default:
92 | printf(
93 | "Usage: %s [-?hvt] [-c config] [-p pidfile]\n"
94 | " -h, -? this message\n"
95 | " -v print version\n"
96 | " -t test config syntax\n"
97 | " -p write pid to pidfile\n",
98 | argv[0]);
99 | return (opt == '?' || opt == 'h') ? EXIT_SUCCESS : EXIT_FAILURE;
100 | }
101 | }
102 |
103 | if (event_get_struct_event_size() != sizeof(struct event)) {
104 | puts("libevent event_get_struct_event_size() != sizeof(struct event)! Check `redsocks -v` and recompile redsocks");
105 | return EXIT_FAILURE;
106 | }
107 |
108 | FILE *f = fopen(confname, "r");
109 | if (!f) {
110 | perror("Unable to open config file");
111 | return EXIT_FAILURE;
112 | }
113 |
114 | parser_context* parser = parser_start(f);
115 | if (!parser) {
116 | perror("Not enough memory for parser");
117 | return EXIT_FAILURE;
118 | }
119 |
120 | FOREACH(ss, subsystems)
121 | if ((*ss)->conf_section)
122 | parser_add_section(parser, (*ss)->conf_section);
123 | error = parser_run(parser);
124 | parser_stop(parser);
125 | fclose(f);
126 |
127 | if (error)
128 | return EXIT_FAILURE;
129 |
130 | if (conftest)
131 | return EXIT_SUCCESS;
132 |
133 | struct event_base* evbase = event_init();
134 | memset(terminators, 0, sizeof(terminators));
135 |
136 | FOREACH(ss, subsystems) {
137 | if ((*ss)->init) {
138 | error = (*ss)->init(evbase);
139 | if (error)
140 | goto shutdown;
141 | }
142 | }
143 |
144 | if (pidfile) {
145 | f = fopen(pidfile, "w");
146 | if (!f) {
147 | perror("Unable to open pidfile for write");
148 | return EXIT_FAILURE;
149 | }
150 | fprintf(f, "%d\n", getpid());
151 | fclose(f);
152 | }
153 |
154 | assert(SIZEOF_ARRAY(exit_signals) == SIZEOF_ARRAY(terminators));
155 | for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) {
156 | signal_set(&terminators[i], exit_signals[i], terminate, NULL);
157 | if (signal_add(&terminators[i], NULL) != 0) {
158 | log_errno(LOG_ERR, "signal_add");
159 | goto shutdown;
160 | }
161 | }
162 |
163 | if (LIBEVENT_VERSION_NUMBER != event_get_version_number()) {
164 | log_error(LOG_WARNING, "libevent version mismatch! headers %8x, runtime %8x\n", LIBEVENT_VERSION_NUMBER, event_get_version_number());
165 | }
166 |
167 | log_error(LOG_NOTICE, "redsocks started, conn_max=%u", redsocks_conn_max());
168 |
169 | event_dispatch();
170 |
171 | log_error(LOG_NOTICE, "redsocks goes down");
172 |
173 | shutdown:
174 | for (i = 0; i < SIZEOF_ARRAY(exit_signals); i++) {
175 | if (signal_initialized(&terminators[i])) {
176 | if (signal_del(&terminators[i]) != 0)
177 | log_errno(LOG_WARNING, "signal_del");
178 | memset(&terminators[i], 0, sizeof(terminators[i]));
179 | }
180 | }
181 |
182 | for (--ss; ss >= subsystems; ss--)
183 | if ((*ss)->fini)
184 | (*ss)->fini();
185 |
186 | event_base_free(evbase);
187 |
188 | return !error ? EXIT_SUCCESS : EXIT_FAILURE;
189 | }
190 |
191 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
192 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
193 |
--------------------------------------------------------------------------------
/main.h:
--------------------------------------------------------------------------------
1 | #ifndef MAIN_H_TUE_JAN_23_15_38_25_2007
2 | #define MAIN_H_TUE_JAN_23_15_38_25_2007
3 |
4 | #include "parser.h"
5 |
6 | struct event_base;
7 |
8 | typedef struct app_subsys_t {
9 | int (*init)(struct event_base*);
10 | int (*fini)();
11 | parser_section* conf_section;
12 | } app_subsys;
13 |
14 | #define SIZEOF_ARRAY(arr) (sizeof(arr) / sizeof(arr[0]))
15 | #define FOREACH(ptr, array) for (ptr = array; ptr < array + SIZEOF_ARRAY(array); ptr++)
16 | #define FOREACH_REV(ptr, array) for (ptr = array + SIZEOF_ARRAY(array) - 1; ptr >= array; ptr--)
17 |
18 |
19 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
20 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
21 | #endif /* MAIN_H_TUE_JAN_23_15_38_25_2007 */
22 |
--------------------------------------------------------------------------------
/md5.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
3 |
4 | This software is provided 'as-is', without any express or implied
5 | warranty. In no event will the authors be held liable for any damages
6 | arising from the use of this software.
7 |
8 | Permission is granted to anyone to use this software for any purpose,
9 | including commercial applications, and to alter it and redistribute it
10 | freely, subject to the following restrictions:
11 |
12 | 1. The origin of this software must not be misrepresented; you must not
13 | claim that you wrote the original software. If you use this software
14 | in a product, an acknowledgment in the product documentation would be
15 | appreciated but is not required.
16 | 2. Altered source versions must be plainly marked as such, and must not be
17 | misrepresented as being the original software.
18 | 3. This notice may not be removed or altered from any source distribution.
19 |
20 | L. Peter Deutsch
21 | ghost@aladdin.com
22 |
23 | */
24 | /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
25 | /*
26 | Independent implementation of MD5 (RFC 1321).
27 |
28 | This code implements the MD5 Algorithm defined in RFC 1321, whose
29 | text is available at
30 | http://www.ietf.org/rfc/rfc1321.txt
31 | The code is derived from the text of the RFC, including the test suite
32 | (section A.5) but excluding the rest of Appendix A. It does not include
33 | any code or documentation that is identified in the RFC as being
34 | copyrighted.
35 |
36 | The original and principal author of md5.h is L. Peter Deutsch
37 | . Other authors are noted in the change history
38 | that follows (in reverse chronological order):
39 |
40 | 2002-04-13 lpd Removed support for non-ANSI compilers; removed
41 | references to Ghostscript; clarified derivation from RFC 1321;
42 | now handles byte order either statically or dynamically.
43 | 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
44 | 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
45 | added conditionalization for C++ compilation from Martin
46 | Purschke .
47 | 1999-05-03 lpd Original version.
48 | */
49 |
50 | #ifndef md5_INCLUDED
51 | # define md5_INCLUDED
52 |
53 | /*
54 | * This package supports both compile-time and run-time determination of CPU
55 | * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
56 | * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
57 | * defined as non-zero, the code will be compiled to run only on big-endian
58 | * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
59 | * run on either big- or little-endian CPUs, but will run slightly less
60 | * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
61 | */
62 |
63 | typedef unsigned char md5_byte_t; /* 8-bit byte */
64 | typedef unsigned int md5_word_t; /* 32-bit word */
65 |
66 | /* Define the state of the MD5 Algorithm. */
67 | typedef struct md5_state_s {
68 | md5_word_t count[2]; /* message length in bits, lsw first */
69 | md5_word_t abcd[4]; /* digest buffer */
70 | md5_byte_t buf[64]; /* accumulate block */
71 | } md5_state_t;
72 |
73 | #ifdef __cplusplus
74 | extern "C"
75 | {
76 | #endif
77 |
78 | /* Initialize the algorithm. */
79 | void md5_init(md5_state_t *pms);
80 |
81 | /* Append a string to the message. */
82 | void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
83 |
84 | /* Finish the message and return the digest. */
85 | void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
86 |
87 | #ifdef __cplusplus
88 | } /* end extern "C" */
89 | #endif
90 |
91 | #endif /* md5_INCLUDED */
92 |
--------------------------------------------------------------------------------
/parser.h:
--------------------------------------------------------------------------------
1 | #ifndef PARSER_H_THU_JAN_11_04_49_38_2007
2 | #define PARSER_H_THU_JAN_11_04_49_38_2007
3 |
4 | #include
5 | #include
6 |
7 | enum disclose_src_e {
8 | DISCLOSE_NONE,
9 | DISCLOSE_X_FORWARDED_FOR,
10 | DISCLOSE_FORWARDED_IP,
11 | DISCLOSE_FORWARDED_IPPORT,
12 | };
13 |
14 | enum on_proxy_fail_e {
15 | ONFAIL_CLOSE,
16 | ONFAIL_FORWARD_HTTP_ERR,
17 | };
18 |
19 | typedef enum {
20 | pt_bool, // "bool" from stdbool.h, not "_Bool" or anything else
21 | pt_pchar,
22 | pt_uint16,
23 | pt_uint32,
24 | pt_in_addr,
25 | pt_in_addr2, // inaddr[0] = net, inaddr[1] = netmask
26 | pt_disclose_src,
27 | pt_on_proxy_fail,
28 | pt_obsolete,
29 | pt_redsocks_max_accept_backoff,
30 | } parser_type;
31 |
32 | typedef struct parser_entry_t {
33 | const char *key;
34 | parser_type type;
35 | void *addr;
36 | } parser_entry;
37 |
38 |
39 | typedef struct parser_context_t parser_context;
40 |
41 |
42 | typedef struct parser_section_t parser_section;
43 | typedef int (*parser_section_onenter)(parser_section *section);
44 | typedef int (*parser_section_onexit)(parser_section *section);
45 |
46 | struct parser_section_t {
47 | parser_section *next;
48 | parser_context *context;
49 | const char *name;
50 | parser_section_onenter onenter; // is called on entry to section
51 | parser_section_onexit onexit; // is called on exit from section
52 | parser_entry *entries;
53 | void *data;
54 | };
55 |
56 |
57 |
58 | parser_context* parser_start(FILE *fd);
59 | void parser_add_section(parser_context *context, parser_section *section);
60 | int parser_run(parser_context *context);
61 | void parser_error(parser_context *context, const char *fmt, ...)
62 | #if defined(__GNUC__)
63 | __attribute__ (( format (printf, 2, 3) ))
64 | #endif
65 | ;
66 | void parser_stop(parser_context *context);
67 |
68 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
69 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
70 | #endif /* PARSER_H_THU_JAN_11_04_49_38_2007 */
71 |
--------------------------------------------------------------------------------
/redsocks.conf.example:
--------------------------------------------------------------------------------
1 | base {
2 | // debug: connection progress
3 | log_debug = off;
4 |
5 | // info: start and end of client session
6 | log_info = on;
7 |
8 | /* possible `log' values are:
9 | * stderr
10 | * "file:/path/to/file"
11 | * syslog:FACILITY facility is any of "daemon", "local0"..."local7"
12 | */
13 | log = stderr;
14 | // log = "file:/path/to/file";
15 | // log = "syslog:local7";
16 |
17 | // detach from console
18 | daemon = off;
19 |
20 | /* Change uid, gid and root directory, these options require root
21 | * privilegies on startup.
22 | * Note, your chroot may requre /etc/localtime if you write log to syslog.
23 | * Log is opened before chroot & uid changing.
24 | * Debian, Ubuntu and some other distributions use `nogroup` instead of
25 | * `nobody`, so change it according to your system if you want redsocks
26 | * to drop root privileges.
27 | */
28 | // user = nobody;
29 | // group = nobody;
30 | // chroot = "/var/chroot";
31 |
32 | /* possible `redirector' values are:
33 | * iptables - for Linux
34 | * ipf - for FreeBSD
35 | * pf - for OpenBSD
36 | * generic - some generic redirector that MAY work
37 | */
38 | redirector = iptables;
39 |
40 | /* Override per-socket values for TCP_KEEPIDLE, TCP_KEEPCNT,
41 | * and TCP_KEEPINTVL. see man 7 tcp for details.
42 | * `redsocks' relies on SO_KEEPALIVE option heavily. */
43 | //tcp_keepalive_time = 0;
44 | //tcp_keepalive_probes = 0;
45 | //tcp_keepalive_intvl = 0;
46 |
47 | // Every `redsocks` connection needs two file descriptors for sockets.
48 | // If `splice` is enabled, it also needs four file descriptors for
49 | // pipes. `redudp` is not accounted at the moment. When max number of
50 | // connection is reached, redsocks tries to close idle connections. If
51 | // there are no idle connections, it stops accept()'ing new
52 | // connections, although kernel continues to fill listenq.
53 |
54 | // Set maximum number of open file descriptors (also known as `ulimit -n`).
55 | // 0 -- do not modify startup limit (default)
56 | // rlimit_nofile = 0;
57 |
58 | // Set maximum number of served connections. Default is to deduce safe
59 | // limit from `splice` setting and RLIMIT_NOFILE.
60 | // redsocks_conn_max = 0;
61 |
62 | // Close connections idle for N seconds when/if connection count
63 | // limit is hit.
64 | // 0 -- do not close idle connections
65 | // 7440 -- 2 hours 4 minutes, see RFC 5382 (default)
66 | // connpres_idle_timeout = 7440;
67 |
68 | // `max_accept_backoff` is a delay in milliseconds to retry `accept()`
69 | // after failure (e.g. due to lack of file descriptors). It's just a
70 | // safety net for misconfigured `redsocks_conn_max`, you should tune
71 | // redsocks_conn_max if accept backoff happens.
72 | // max_accept_backoff = 60000;
73 | }
74 |
75 | redsocks {
76 | /* `local_ip' defaults to 127.0.0.1 for security reasons,
77 | * use 0.0.0.0 if you want to listen on every interface.
78 | * `local_*' are used as port to redirect to.
79 | */
80 | local_ip = 127.0.0.1;
81 | local_port = 12345;
82 |
83 | // listen() queue length. Default value is SOMAXCONN and it should be
84 | // good enough for most of us.
85 | // listenq = 128; // SOMAXCONN equals 128 on my Linux box.
86 |
87 | // Enable or disable faster data pump based on splice(2) syscall.
88 | // Default value depends on your kernel version, true for 2.6.27.13+
89 | // splice = false;
90 |
91 | // `ip' and `port' are IP and tcp-port of proxy-server
92 | // You can also use hostname instead of IP, only one (random)
93 | // address of multihomed host will be used.
94 | ip = example.org;
95 | port = 1080;
96 |
97 | // known types: socks4, socks5, http-connect, http-relay
98 | type = socks5;
99 |
100 | // login = "foobar";
101 | // password = "baz";
102 |
103 | // known ways to disclose client IP to the proxy:
104 | // false -- disclose nothing
105 | // http-connect supports:
106 | // X-Forwarded-For -- X-Forwarded-For: IP
107 | // Forwarded_ip -- Forwarded: for=IP # see RFC7239
108 | // Forwarded_ipport -- Forwarded: for="IP:port" # see RFC7239
109 | // disclose_src = false;
110 |
111 | // various ways to handle proxy failure
112 | // close -- just close connection (default)
113 | // forward_http_err -- forward HTTP error page from proxy as-is
114 | // on_proxy_fail = close;
115 | }
116 |
117 | redudp {
118 | // `local_ip' should not be 0.0.0.0 as it's also used for outgoing
119 | // packets that are sent as replies - and it should be fixed
120 | // if we want NAT to work properly.
121 | local_ip = 127.0.0.1;
122 | local_port = 10053;
123 |
124 | // `ip' and `port' of socks5 proxy server.
125 | ip = 10.0.0.1;
126 | port = 1080;
127 | login = username;
128 | password = pazzw0rd;
129 |
130 | // redsocks knows about two options while redirecting UDP packets at
131 | // linux: TPROXY and REDIRECT. TPROXY requires more complex routing
132 | // configuration and fresh kernel (>= 2.6.37 according to squid
133 | // developers[1]) but has hack-free way to get original destination
134 | // address, REDIRECT is easier to configure, but requires `dest_ip` and
135 | // `dest_port` to be set, limiting packet redirection to single
136 | // destination.
137 | // [1] http://wiki.squid-cache.org/Features/Tproxy4
138 | dest_ip = 8.8.8.8;
139 | dest_port = 53;
140 |
141 | udp_timeout = 30;
142 | udp_timeout_stream = 180;
143 | }
144 |
145 | dnstc {
146 | // fake and really dumb DNS server that returns "truncated answer" to
147 | // every query via UDP, RFC-compliant resolver should repeat same query
148 | // via TCP in this case.
149 | local_ip = 127.0.0.1;
150 | local_port = 5300;
151 | }
152 |
153 | dnsu2t {
154 | // fake and a bit less dumb DNS server that converts several UDP
155 | // queries into single pipelined TCP stream of DNS queries.
156 | local_ip = 127.0.0.1;
157 | local_port = 5313;
158 |
159 | // See https://en.wikipedia.org/wiki/Public_recursive_name_server
160 | // NB: TCP connection to this ${ip}:${port} is not passed through
161 | // proxy, configure your firewall rules if you want that.
162 | remote_ip = 8.8.8.8;
163 | remote_port = 53;
164 |
165 | // Maximum amount of concurrent in-flight DNS queries sent to remote server.
166 | // Some public DNS servers seem to limit it and terminate connections
167 | // with high count of in-flight requests, so it's trade-off between
168 | // request latency and availability. In-flight requests are not cached,
169 | // so they're lost in case of DNS/TCP connection termination.
170 | // inflight_max = 16;
171 |
172 | // I/O timeout of remote endpoint. Default value is quite conservative and
173 | // corresponds to the highest timeout among public servers from Wikipedia.
174 | // remote_timeout = 30;
175 | }
176 |
177 | // you can add more `redsocks' and `redudp' sections if you need.
178 |
--------------------------------------------------------------------------------
/redsocks.h:
--------------------------------------------------------------------------------
1 | #ifndef REDSOCKS_H_WED_JAN_24_22_17_11_2007
2 | #define REDSOCKS_H_WED_JAN_24_22_17_11_2007
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "list.h"
9 | #include "parser.h"
10 | #include "apple.h"
11 |
12 | struct redsocks_client_t;
13 | struct redsocks_instance_t;
14 |
15 | typedef struct relay_subsys_t {
16 | char *name;
17 | size_t payload_len; // size of relay-specific data in client section
18 | size_t instance_payload_len; // size of relay-specify data in instance section
19 | evbuffercb readcb;
20 | evbuffercb writecb;
21 | void (*init)(struct redsocks_client_t *client);
22 | void (*fini)(struct redsocks_client_t *client);
23 | void (*instance_init)(struct redsocks_instance_t *instance);
24 | void (*instance_fini)(struct redsocks_instance_t *instance);
25 | // connect_relay (if any) is called instead of redsocks_connect_relay after client connection acceptance
26 | void (*connect_relay)(struct redsocks_client_t *client);
27 | } relay_subsys;
28 |
29 | typedef struct redsocks_config_t {
30 | struct sockaddr_in bindaddr;
31 | struct sockaddr_in relayaddr;
32 | char *type;
33 | char *login;
34 | char *password;
35 | uint16_t listenq;
36 | bool use_splice;
37 | enum disclose_src_e disclose_src;
38 | enum on_proxy_fail_e on_proxy_fail;
39 | } redsocks_config;
40 |
41 | typedef struct redsocks_instance_t {
42 | list_head list;
43 | redsocks_config config;
44 | struct event listener;
45 | list_head clients;
46 | relay_subsys *relay_ss;
47 | } redsocks_instance;
48 |
49 | typedef unsigned short evshut_t; // EV_READ | EV_WRITE
50 |
51 | typedef struct redsocks_client_t {
52 | list_head list;
53 | redsocks_instance *instance;
54 | struct bufferevent *client;
55 | struct bufferevent *relay;
56 | struct sockaddr_in clientaddr;
57 | struct sockaddr_in destaddr;
58 | int state; // it's used by bottom layer
59 | evshut_t client_evshut;
60 | evshut_t relay_evshut;
61 | struct timeval first_event;
62 | struct timeval last_event;
63 | } redsocks_client;
64 |
65 | typedef struct splice_pipe_t {
66 | int read;
67 | int write;
68 | size_t size;
69 | } splice_pipe;
70 |
71 | typedef struct redsocks_pump_t {
72 | /* Quick-n-dirty test show, that some Linux 4.4.0 build uses ~1.5 kb of
73 | * slab_unreclaimable RAM per every pipe pair. Most of connections are
74 | * usually idle and it's possble to save some measurable amount of RAM
75 | * using shared pipe pool. */
76 | redsocks_client c;
77 | splice_pipe request;
78 | splice_pipe reply;
79 | struct event client_read;
80 | struct event client_write;
81 | struct event relay_read;
82 | struct event relay_write;
83 | } redsocks_pump;
84 |
85 | static inline size_t sizeof_client(redsocks_instance *i)
86 | {
87 | return ((i->config.use_splice) ? sizeof(redsocks_pump) : sizeof(redsocks_client)) + i->relay_ss->payload_len;
88 | }
89 |
90 | static inline void* red_payload(redsocks_client *c)
91 | {
92 | return (c->instance->config.use_splice) ? (void*)(((redsocks_pump*)c) + 1) : (void*)(c + 1);
93 | }
94 |
95 | static inline redsocks_pump* red_pump(redsocks_client *c)
96 | {
97 | assert(c->instance->config.use_splice);
98 | return (redsocks_pump*)c;
99 | }
100 |
101 | void redsocks_shutdown(redsocks_client *client, struct bufferevent *buffev, int how);
102 | void redsocks_drop_client(redsocks_client *client);
103 | void redsocks_touch_client(redsocks_client *client);
104 | void redsocks_connect_relay(redsocks_client *client);
105 | void redsocks_start_relay(redsocks_client *client);
106 | bool redsocks_has_splice_instance();
107 |
108 | typedef int (*size_comparator)(size_t a, size_t b);
109 | int sizes_equal(size_t a, size_t b);
110 | int sizes_greater_equal(size_t a, size_t b);
111 | /** helper for functions when we expect ONLY reply of some size and anything else is error
112 | */
113 | int redsocks_read_expected(redsocks_client *client, struct evbuffer *input, void *data, size_comparator comparator, size_t expected);
114 |
115 | typedef struct evbuffer* (*redsocks_message_maker)(redsocks_client *client);
116 | typedef struct evbuffer* (*redsocks_message_maker_plain)(void *p);
117 | struct evbuffer *mkevbuffer(void *data, size_t len);
118 | /* Yahoo! This code is ex-plain! :-D */
119 | int redsocks_write_helper_ex_plain(
120 | struct bufferevent *buffev, redsocks_client *client,
121 | redsocks_message_maker_plain mkmessage, void *p, int state, size_t wm_low, size_t wm_high);
122 | int redsocks_write_helper_ex(
123 | struct bufferevent *buffev, redsocks_client *client,
124 | redsocks_message_maker mkmessage, int state, size_t wm_low, size_t wm_high);
125 | int redsocks_write_helper(
126 | struct bufferevent *buffev, redsocks_client *client,
127 | redsocks_message_maker mkmessage, int state, size_t wm_only);
128 |
129 |
130 | #define redsocks_close(fd) redsocks_close_internal((fd), __FILE__, __LINE__, __func__)
131 | void redsocks_close_internal(int fd, const char* file, int line, const char *func);
132 |
133 | #define redsocks_event_add(client, ev) redsocks_event_add_internal((client), (ev), __FILE__, __LINE__, __func__)
134 | void redsocks_event_add_internal(redsocks_client *client, struct event *ev, const char *file, int line, const char *func);
135 |
136 | #define redsocks_event_del(client, ev) redsocks_event_del_internal((client), (ev), __FILE__, __LINE__, __func__)
137 | void redsocks_event_del_internal(redsocks_client *client, struct event *ev, const char *file, int line, const char *func);
138 |
139 | #define redsocks_bufferevent_dropfd(client, ev) redsocks_bufferevent_dropfd_internal((client), (ev), __FILE__, __LINE__, __func__)
140 | void redsocks_bufferevent_dropfd_internal(redsocks_client *client, struct bufferevent *ev, const char *file, int line, const char *func);
141 |
142 | // I have to account descriptiors for accept-backoff, that's why BEV_OPT_CLOSE_ON_FREE is not used.
143 | void redsocks_bufferevent_free(struct bufferevent *buffev);
144 |
145 | #define redsocks_log_error(client, prio, msg...) \
146 | redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, &(client)->destaddr, prio, ## msg)
147 | #define redsocks_log_errno(client, prio, msg...) \
148 | redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, &(client)->destaddr, prio, ## msg)
149 | void redsocks_log_write_plain(
150 | const char *file, int line, const char *func, int do_errno,
151 | const struct sockaddr_in *clientaddr, const struct sockaddr_in *destaddr,
152 | int priority, const char *fmt, ...)
153 | #if defined(__GNUC__)
154 | __attribute__ (( format (printf, 8, 9) ))
155 | #endif
156 | ;
157 |
158 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
159 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
160 | #endif /* REDSOCKS_H_WED_JAN_24_22_17_11_2007 */
161 |
162 |
--------------------------------------------------------------------------------
/redsocks.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Transparent redirector of any TCP connection to proxy using your firewall
3 |
4 | [Service]
5 | Type=forking
6 | PIDFile=/run/redsocks/redsocks.pid
7 | EnvironmentFile=/etc/conf.d/redsocks
8 | User=redsocks
9 | ExecStartPre=/usr/bin/redsocks -t -c $REDSOCKS_CONF
10 | ExecStart=/usr/bin/redsocks -c $REDSOCKS_CONF \
11 | -p /run/redsocks/redsocks.pid
12 | ExecStopPost=/bin/rm /run/redsocks/redsocks.pid
13 | Restart=on-abort
14 |
15 | [Install]
16 | WantedBy=multi-user.target
17 |
--------------------------------------------------------------------------------
/redudp.h:
--------------------------------------------------------------------------------
1 | #ifndef REDUDP_H
2 | #define REDUDP_H
3 |
4 | typedef struct redudp_config_t {
5 | struct sockaddr_in bindaddr;
6 | struct sockaddr_in relayaddr;
7 | // TODO: outgoingaddr;
8 | struct sockaddr_in destaddr;
9 | char *login;
10 | char *password;
11 | uint16_t max_pktqueue;
12 | uint16_t udp_timeout;
13 | uint16_t udp_timeout_stream;
14 | } redudp_config;
15 |
16 | typedef struct redudp_instance_t {
17 | list_head list;
18 | redudp_config config;
19 | struct event listener;
20 | list_head clients;
21 | } redudp_instance;
22 |
23 | typedef struct redudp_client_t {
24 | list_head list;
25 | redudp_instance *instance;
26 | struct sockaddr_in clientaddr;
27 | struct sockaddr_in destaddr;
28 | int sender_fd; // shared between several clients socket (bound to `destaddr`)
29 | struct event timeout;
30 | struct bufferevent *relay;
31 | struct event udprelay;
32 | struct sockaddr_in udprelayaddr;
33 | int state; // it's used by bottom layer
34 | time_t first_event;
35 | time_t last_client_event;
36 | time_t last_relay_event;
37 | unsigned int queue_len;
38 | list_head queue;
39 | } redudp_client;
40 |
41 | typedef struct enqueued_packet_t {
42 | list_head list;
43 | size_t len;
44 | char data[1];
45 | } enqueued_packet;
46 |
47 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
48 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
49 | #endif /* REDUDP_H */
50 |
--------------------------------------------------------------------------------
/socks4.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include "log.h"
21 | #include "redsocks.h"
22 |
23 | typedef enum socks4_state_t {
24 | socks4_new,
25 | socks4_request_sent,
26 | socks4_reply_came,
27 | socks4_MAX,
28 | } socks4_state;
29 |
30 | typedef struct socks4_req_t {
31 | uint8_t ver;
32 | uint8_t cmd;
33 | uint16_t port;
34 | uint32_t addr;
35 | char login[1]; // we need at least zero-byte
36 | } PACKED socks4_req;
37 |
38 | const int socks4_ver = 4;
39 | const int socks4_cmd_connect = 1;
40 | const int socks4_cmd_bind = 2;
41 |
42 | typedef struct socks4_reply_t {
43 | uint8_t ver;
44 | uint8_t status;
45 | uint16_t port;
46 | uint32_t addr;
47 | } PACKED socks4_reply;
48 |
49 | const int socks4_status_ok = 90;
50 | const int socks4_status_fail = 91;
51 | const int socks4_status_no_ident = 92;
52 | const int socks4_status_fake_ident = 93;
53 |
54 |
55 | static void socks4_instance_init(redsocks_instance *instance)
56 | {
57 | if (instance->config.password)
58 | log_error(LOG_WARNING, "password <%s> is ignored for socks4 connections", instance->config.password);
59 | }
60 |
61 | static void socks4_client_init(redsocks_client *client)
62 | {
63 | client->state = socks4_new;
64 | }
65 |
66 |
67 | static void socks4_read_cb(struct bufferevent *buffev, void *_arg)
68 | {
69 | redsocks_client *client = _arg;
70 |
71 | assert(client->state >= socks4_request_sent);
72 |
73 | redsocks_touch_client(client);
74 |
75 | if (client->state == socks4_request_sent) {
76 | socks4_reply reply;
77 |
78 | if (redsocks_read_expected(client, buffev->input, &reply, sizes_greater_equal, sizeof(reply)) < 0)
79 | return;
80 |
81 | client->state = socks4_reply_came;
82 | if (reply.ver != 0) {
83 | redsocks_log_error(client, LOG_NOTICE, "Socks4 server reported unexpected reply version...");
84 | redsocks_drop_client(client);
85 | }
86 | else if (reply.status == socks4_status_ok)
87 | redsocks_start_relay(client);
88 | else {
89 | redsocks_log_error(client, LOG_NOTICE, "Socks4 server status: %s (%i)",
90 | reply.status == socks4_status_fail ? "fail" :
91 | reply.status == socks4_status_no_ident ? "no ident" :
92 | reply.status == socks4_status_fake_ident ? "fake ident" : "?",
93 | reply.status);
94 | redsocks_drop_client(client);
95 | }
96 | }
97 | }
98 |
99 | static struct evbuffer *socks4_mkconnect(redsocks_client *client)
100 | {
101 | const redsocks_config *config = &client->instance->config;
102 | const char *username = config->login ? config->login : "";
103 | // space for \0 comes from socks4_req->login
104 | size_t username_len = strlen(username);
105 | size_t len = sizeof(socks4_req) + username_len;
106 | socks4_req *req = calloc(1, len);
107 |
108 | req->ver = socks4_ver;
109 | req->cmd = socks4_cmd_connect;
110 | req->port = client->destaddr.sin_port;
111 | req->addr = client->destaddr.sin_addr.s_addr;
112 | memcpy(req->login, username, username_len + 1);
113 |
114 | struct evbuffer *ret = mkevbuffer(req, len);
115 | free(req);
116 | return ret;
117 | }
118 |
119 | static void socks4_write_cb(struct bufferevent *buffev, void *_arg)
120 | {
121 | redsocks_client *client = _arg;
122 |
123 | redsocks_touch_client(client);
124 |
125 | if (client->state == socks4_new) {
126 | redsocks_write_helper(
127 | buffev, client,
128 | socks4_mkconnect, socks4_request_sent, sizeof(socks4_reply)
129 | );
130 | }
131 | else if (client->state >= socks4_request_sent) {
132 | bufferevent_disable(buffev, EV_WRITE);
133 | }
134 | }
135 |
136 |
137 | relay_subsys socks4_subsys =
138 | {
139 | .name = "socks4",
140 | .payload_len = 0,
141 | .instance_payload_len = 0,
142 | .readcb = socks4_read_cb,
143 | .writecb = socks4_write_cb,
144 | .init = socks4_client_init,
145 | .instance_init = socks4_instance_init,
146 | };
147 |
148 |
149 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
150 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
151 |
--------------------------------------------------------------------------------
/socks5.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include "utils.h"
22 | #include "log.h"
23 | #include "redsocks.h"
24 | #include "socks5.h"
25 |
26 | typedef enum socks5_state_t {
27 | socks5_new,
28 | socks5_method_sent,
29 | socks5_auth_sent,
30 | socks5_request_sent,
31 | socks5_skip_domain,
32 | socks5_skip_address,
33 | socks5_MAX,
34 | } socks5_state;
35 |
36 | typedef struct socks5_client_t {
37 | int do_password; // 1 - password authentication is possible
38 | int to_skip; // valid while reading last reply (after main request)
39 | } socks5_client;
40 |
41 | const char *socks5_strstatus[] = {
42 | "ok",
43 | "server failure",
44 | "connection not allowed by ruleset",
45 | "network unreachable",
46 | "host unreachable",
47 | "connection refused",
48 | "TTL expired",
49 | "command not supported",
50 | "address type not supported",
51 | };
52 | const size_t socks5_strstatus_len = SIZEOF_ARRAY(socks5_strstatus);
53 |
54 | const char* socks5_status_to_str(int socks5_status)
55 | {
56 | if (0 <= socks5_status && socks5_status < socks5_strstatus_len) {
57 | return socks5_strstatus[socks5_status];
58 | }
59 | else {
60 | return "";
61 | }
62 | }
63 |
64 | bool socks5_is_valid_cred(const char *login, const char *password)
65 | {
66 | if (!login || !password)
67 | return false;
68 | if (strlen(login) > 255) {
69 | log_error(LOG_WARNING, "Socks5 login can't be more than 255 chars, <%s> is too long", login);
70 | return false;
71 | }
72 | if (strlen(password) > 255) {
73 | log_error(LOG_WARNING, "Socks5 password can't be more than 255 chars, <%s> is too long", password);
74 | return false;
75 | }
76 | return true;
77 | }
78 |
79 | static void socks5_instance_init(redsocks_instance *instance)
80 | {
81 | redsocks_config *config = &instance->config;
82 | if (config->login || config->password) {
83 | bool deauth = false;
84 | if (config->login && config->password) {
85 | deauth = ! socks5_is_valid_cred(config->login, config->password);
86 | } else {
87 | log_error(LOG_WARNING, "Socks5 needs either both login and password or none of them");
88 | deauth = true;
89 | }
90 | if (deauth) {
91 | free(config->login);
92 | free(config->password);
93 | config->login = config->password = NULL;
94 | }
95 | }
96 | }
97 |
98 | static void socks5_client_init(redsocks_client *client)
99 | {
100 | socks5_client *socks5 = red_payload(client);
101 | const redsocks_config *config = &client->instance->config;
102 |
103 | client->state = socks5_new;
104 | socks5->do_password = (config->login && config->password) ? 1 : 0;
105 | }
106 |
107 | static struct evbuffer *socks5_mkmethods(redsocks_client *client)
108 | {
109 | socks5_client *socks5 = red_payload(client);
110 | return socks5_mkmethods_plain(socks5->do_password);
111 | }
112 |
113 | struct evbuffer *socks5_mkmethods_plain(int do_password)
114 | {
115 | assert(do_password == 0 || do_password == 1);
116 | int len = sizeof(socks5_method_req) + do_password;
117 | socks5_method_req *req = calloc(1, len);
118 |
119 | req->ver = socks5_ver;
120 | req->num_methods = 1 + do_password;
121 | req->methods[0] = socks5_auth_none;
122 | if (do_password)
123 | req->methods[1] = socks5_auth_password;
124 |
125 | struct evbuffer *ret = mkevbuffer(req, len);
126 | free(req);
127 | return ret;
128 | }
129 |
130 | static struct evbuffer *socks5_mkpassword(redsocks_client *client)
131 | {
132 | return socks5_mkpassword_plain(client->instance->config.login, client->instance->config.password);
133 | }
134 |
135 | struct evbuffer *socks5_mkpassword_plain(const char *login, const char *password)
136 | {
137 | size_t ulen = strlen(login);
138 | size_t plen = strlen(password);
139 | size_t length = 1 /* version */ + 1 + ulen + 1 + plen;
140 | uint8_t req[length];
141 |
142 | req[0] = socks5_password_ver; // RFC 1929 says so
143 | req[1] = ulen;
144 | memcpy(&req[2], login, ulen);
145 | req[2+ulen] = plen;
146 | memcpy(&req[3+ulen], password, plen);
147 | return mkevbuffer(req, length);
148 | }
149 |
150 | struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr)
151 | {
152 | struct {
153 | socks5_req head;
154 | socks5_addr_ipv4 ip;
155 | } PACKED req;
156 |
157 | assert(destaddr->sin_family == AF_INET);
158 |
159 | req.head.ver = socks5_ver;
160 | req.head.cmd = socks5_cmd;
161 | req.head.reserved = 0;
162 | req.head.addrtype = socks5_addrtype_ipv4;
163 | req.ip.addr = destaddr->sin_addr.s_addr;
164 | req.ip.port = destaddr->sin_port;
165 | return mkevbuffer(&req, sizeof(req));
166 | }
167 |
168 | static struct evbuffer *socks5_mkconnect(redsocks_client *client)
169 | {
170 | return socks5_mkcommand_plain(socks5_cmd_connect, &client->destaddr);
171 | }
172 |
173 | static void socks5_write_cb(struct bufferevent *buffev, void *_arg)
174 | {
175 | redsocks_client *client = _arg;
176 |
177 | redsocks_touch_client(client);
178 |
179 | if (client->state == socks5_new) {
180 | redsocks_write_helper(
181 | buffev, client,
182 | socks5_mkmethods, socks5_method_sent, sizeof(socks5_method_reply)
183 | );
184 | }
185 | }
186 |
187 | const char* socks5_is_known_auth_method(socks5_method_reply *reply, int do_password)
188 | {
189 | if (reply->ver != socks5_ver)
190 | return "Socks5 server reported unexpected auth methods reply version...";
191 | else if (reply->method == socks5_auth_invalid)
192 | return "Socks5 server refused all our auth methods.";
193 | else if (reply->method != socks5_auth_none && !(reply->method == socks5_auth_password && do_password))
194 | return "Socks5 server requested unexpected auth method...";
195 | else
196 | return NULL;
197 | }
198 |
199 | static void socks5_read_auth_methods(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5)
200 | {
201 | socks5_method_reply reply;
202 | const char *error = NULL;
203 |
204 | if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0)
205 | return;
206 |
207 | error = socks5_is_known_auth_method(&reply, socks5->do_password);
208 | if (error) {
209 | redsocks_log_error(client, LOG_NOTICE, "socks5_is_known_auth_method: %s", error);
210 | redsocks_drop_client(client);
211 | }
212 | else if (reply.method == socks5_auth_none) {
213 | redsocks_write_helper(
214 | buffev, client,
215 | socks5_mkconnect, socks5_request_sent, sizeof(socks5_reply)
216 | );
217 | }
218 | else if (reply.method == socks5_auth_password) {
219 | redsocks_write_helper(
220 | buffev, client,
221 | socks5_mkpassword, socks5_auth_sent, sizeof(socks5_auth_reply)
222 | );
223 | }
224 | }
225 |
226 | static void socks5_read_auth_reply(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5)
227 | {
228 | socks5_auth_reply reply;
229 |
230 | if (redsocks_read_expected(client, buffev->input, &reply, sizes_equal, sizeof(reply)) < 0)
231 | return;
232 |
233 | if (reply.ver != socks5_password_ver) {
234 | redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected auth reply version %d", reply.ver);
235 | redsocks_drop_client(client);
236 | }
237 | else if (reply.status == socks5_password_passed)
238 | redsocks_write_helper(
239 | buffev, client,
240 | socks5_mkconnect, socks5_request_sent, sizeof(socks5_reply)
241 | );
242 | else {
243 | redsocks_log_error(client, LOG_NOTICE, "Socks5 auth failure, status %i", reply.status);
244 | redsocks_drop_client(client);
245 | }
246 | }
247 |
248 | static void socks5_read_reply(struct bufferevent *buffev, redsocks_client *client, socks5_client *socks5)
249 | {
250 | socks5_reply reply;
251 |
252 | if (redsocks_read_expected(client, buffev->input, &reply, sizes_greater_equal, sizeof(reply)) < 0)
253 | return;
254 |
255 | if (reply.ver != socks5_ver) {
256 | redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version...");
257 | redsocks_drop_client(client);
258 | }
259 | else if (reply.status == socks5_status_succeeded) {
260 | socks5_state nextstate;
261 | size_t len;
262 |
263 | if (reply.addrtype == socks5_addrtype_ipv4) {
264 | len = socks5->to_skip = sizeof(socks5_addr_ipv4);
265 | nextstate = socks5_skip_address;
266 | }
267 | else if (reply.addrtype == socks5_addrtype_ipv6) {
268 | len = socks5->to_skip = sizeof(socks5_addr_ipv6);
269 | nextstate = socks5_skip_address;
270 | }
271 | else if (reply.addrtype == socks5_addrtype_domain) {
272 | socks5_addr_domain domain;
273 | len = sizeof(domain.size);
274 | nextstate = socks5_skip_domain;
275 | }
276 | else {
277 | redsocks_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type...");
278 | redsocks_drop_client(client);
279 | return;
280 | }
281 |
282 | redsocks_write_helper(
283 | buffev, client,
284 | NULL, nextstate, len
285 | );
286 | }
287 | else {
288 | redsocks_log_error(client, LOG_NOTICE, "Socks5 server status: %s (%i)",
289 | /* 0 <= reply.status && */ reply.status < SIZEOF_ARRAY(socks5_strstatus)
290 | ? socks5_strstatus[reply.status] : "?", reply.status);
291 | redsocks_drop_client(client);
292 | }
293 | }
294 |
295 | static void socks5_read_cb(struct bufferevent *buffev, void *_arg)
296 | {
297 | redsocks_client *client = _arg;
298 | socks5_client *socks5 = red_payload(client);
299 |
300 | redsocks_touch_client(client);
301 |
302 | if (client->state == socks5_method_sent) {
303 | socks5_read_auth_methods(buffev, client, socks5);
304 | }
305 | else if (client->state == socks5_auth_sent) {
306 | socks5_read_auth_reply(buffev, client, socks5);
307 | }
308 | else if (client->state == socks5_request_sent) {
309 | socks5_read_reply(buffev, client, socks5);
310 | }
311 | else if (client->state == socks5_skip_domain) {
312 | socks5_addr_ipv4 ipv4; // all socks5_addr*.port are equal
313 | uint8_t size;
314 | if (redsocks_read_expected(client, buffev->input, &size, sizes_greater_equal, sizeof(size)) < 0)
315 | return;
316 | socks5->to_skip = size + sizeof(ipv4.port);
317 | redsocks_write_helper(
318 | buffev, client,
319 | NULL, socks5_skip_address, socks5->to_skip
320 | );
321 | }
322 | else if (client->state == socks5_skip_address) {
323 | uint8_t data[socks5->to_skip];
324 | if (redsocks_read_expected(client, buffev->input, data, sizes_greater_equal, socks5->to_skip) < 0)
325 | return;
326 | redsocks_start_relay(client);
327 | }
328 | else {
329 | redsocks_drop_client(client);
330 | }
331 | }
332 |
333 | relay_subsys socks5_subsys =
334 | {
335 | .name = "socks5",
336 | .payload_len = sizeof(socks5_client),
337 | .instance_payload_len = 0,
338 | .readcb = socks5_read_cb,
339 | .writecb = socks5_write_cb,
340 | .init = socks5_client_init,
341 | .instance_init = socks5_instance_init,
342 | };
343 |
344 |
345 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
346 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
347 |
--------------------------------------------------------------------------------
/socks5.h:
--------------------------------------------------------------------------------
1 | #ifndef SOCKS5_H
2 | #define SOCKS5_H
3 |
4 | #include
5 | #include "utils.h"
6 |
7 | typedef struct socks5_method_req_t {
8 | uint8_t ver;
9 | uint8_t num_methods;
10 | uint8_t methods[1]; // at least one
11 | } PACKED socks5_method_req;
12 |
13 | typedef struct socks5_method_reply_t {
14 | uint8_t ver;
15 | uint8_t method;
16 | } PACKED socks5_method_reply;
17 |
18 | static const int socks5_ver = 5;
19 |
20 | static const int socks5_auth_none = 0x00;
21 | static const int socks5_auth_gssapi = 0x01;
22 | static const int socks5_auth_password = 0x02;
23 | static const int socks5_auth_invalid = 0xFF;
24 |
25 | typedef struct socks5_auth_reply_t {
26 | uint8_t ver;
27 | uint8_t status;
28 | } PACKED socks5_auth_reply;
29 |
30 | static const int socks5_password_ver = 0x01;
31 | static const int socks5_password_passed = 0x00;
32 |
33 |
34 | typedef struct socks5_addr_ipv4_t {
35 | uint32_t addr;
36 | uint16_t port;
37 | } PACKED socks5_addr_ipv4;
38 |
39 | typedef struct socks5_addr_domain_t {
40 | uint8_t size;
41 | uint8_t more[1];
42 | /* uint16_t port; */
43 | } PACKED socks5_addr_domain;
44 |
45 | typedef struct socks5_addr_ipv6_t {
46 | uint8_t addr[16];
47 | uint16_t port;
48 | } PACKED socks5_addr_ipv6;
49 |
50 | typedef struct socks5_req_t {
51 | uint8_t ver;
52 | uint8_t cmd;
53 | uint8_t reserved;
54 | uint8_t addrtype;
55 | /* socks5_addr_* */
56 | } PACKED socks5_req;
57 |
58 | typedef struct socks5_reply_t {
59 | uint8_t ver;
60 | uint8_t status;
61 | uint8_t reserved;
62 | uint8_t addrtype;
63 | /* socks5_addr_* */
64 | } PACKED socks5_reply;
65 |
66 | typedef struct socks5_udp_preabmle_t {
67 | uint16_t reserved;
68 | uint8_t frag_no;
69 | uint8_t addrtype; /* 0x01 for IPv4 */
70 | /* socks5_addr_* */
71 | socks5_addr_ipv4 ip; /* I support only IPv4 at the moment */
72 | } PACKED socks5_udp_preabmle;
73 |
74 | static const int socks5_reply_maxlen = 512; // as domain name can't be longer than 256 bytes
75 | static const int socks5_addrtype_ipv4 = 1;
76 | static const int socks5_addrtype_domain = 3;
77 | static const int socks5_addrtype_ipv6 = 4;
78 | static const int socks5_status_succeeded = 0;
79 | static const int socks5_status_server_failure = 1;
80 | static const int socks5_status_connection_not_allowed_by_ruleset = 2;
81 | static const int socks5_status_Network_unreachable = 3;
82 | static const int socks5_status_Host_unreachable = 4;
83 | static const int socks5_status_Connection_refused = 5;
84 | static const int socks5_status_TTL_expired = 6;
85 | static const int socks5_status_Command_not_supported = 7;
86 | static const int socks5_status_Address_type_not_supported = 8;
87 |
88 |
89 | const char* socks5_status_to_str(int socks5_status);
90 | bool socks5_is_valid_cred(const char *login, const char *password);
91 |
92 | struct evbuffer *socks5_mkmethods_plain(int do_password);
93 | struct evbuffer *socks5_mkpassword_plain(const char *login, const char *password);
94 | const char* socks5_is_known_auth_method(socks5_method_reply *reply, int do_password);
95 |
96 | static const int socks5_cmd_connect = 1;
97 | static const int socks5_cmd_bind = 2;
98 | static const int socks5_cmd_udp_associate = 3;
99 | struct evbuffer *socks5_mkcommand_plain(int socks5_cmd, const struct sockaddr_in *destaddr);
100 |
101 |
102 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
103 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
104 | #endif /* SOCKS5_H */
105 |
--------------------------------------------------------------------------------
/tdestroy.c:
--------------------------------------------------------------------------------
1 | #ifdef __APPLE__
2 |
3 | #define _GNU_SOURCE
4 | #include
5 | #include
6 | #include "tsearch.h"
7 |
8 | void tdestroy(void *root, void (*freekey)(void *))
9 | {
10 | struct node *r = root;
11 |
12 | if (r == 0)
13 | return;
14 | tdestroy(r->a[0], freekey);
15 | tdestroy(r->a[1], freekey);
16 | if (freekey) freekey((void *)r->key);
17 | free(r);
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/tests/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash -ex
2 |
3 | (cd .. && make)
4 | cp -a ../redsocks ./regw/redsocks
5 |
6 | for img in *; do
7 | if [ -d "$img" -a -f "$img/Dockerfile" ]; then
8 | sudo docker build -t redsocks/"$img" ./"$img"/
9 | fi
10 | done
11 |
--------------------------------------------------------------------------------
/tests/cleanup:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 | docker ps
3 | vms=$(echo gw web inetd regw dante-{0..1} squid-{8..9} tank{10..26})
4 | docker stop --time 1 $vms
5 | docker rm $vms
6 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from functools import partial
2 | from multiprocessing.dummy import Pool as ThrPool
3 | from subprocess import check_call, check_output, STDOUT
4 | import multiprocessing
5 | import os
6 | import time
7 |
8 | import pytest
9 |
10 | # 10.0.1.0/24 (br-web) -- web, squid, inetd, gw
11 | # 10.0.2.0/24 (br-tank) -- tank, openwrt
12 | # 10.0.8.0/24 (br-txrx) -- gw, openwrt
13 |
14 | BR = {
15 | '10.0.1': 'web',
16 | '10.0.8': 'txrx',
17 | '10.0.2': 'tank',
18 | }
19 |
20 | GW = {
21 | 'web': '10.0.1.1',
22 | 'txrx': '10.0.8.1',
23 | 'tank': '10.0.2.123',
24 | }
25 |
26 | SLEEPING_BEAST = 'sleep 3600'
27 | DOCKER_CMD = os.environ.get('DOCKER_CMD', '/usr/bin/docker')
28 | PIPEWORK_CMD = os.environ.get('PIPEWORK_CMD', '/usr/bin/pipework')
29 |
30 | class VM(object):
31 | def __init__(self, name, tag, ip4=None, cmd='', docker_opt=''):
32 | self.docker = DOCKER_CMD
33 | self.pipework = PIPEWORK_CMD
34 | self.netns = '/var/run/netns'
35 | self.dns = '8.8.8.8'
36 |
37 | self.name, self.tag = name, tag
38 | self.cmd, self.docker_opt = cmd, docker_opt
39 | if ip4:
40 | self.ip4 = ip4
41 | self.sha = self.output('sudo docker run --detach --dns {dns} --name {name} --hostname {name} {docker_opt} {tag} {cmd}')
42 | self.pid = int(self.output('docker inspect -f {{{{.State.Pid}}}} {sha}'))
43 | if not os.path.exists(self.netns):
44 | self.call('sudo mkdir {netns}')
45 | self.call('sudo ln -sf /proc/{pid}/ns/net {netns}/{name}')
46 | self.net()
47 | while cmd != SLEEPING_BEAST and 'LISTEN' not in self.do('netstat -ltn'):
48 | time.sleep(0.1)
49 |
50 | def net(self):
51 | self.net_noext()
52 | self.net_br()
53 |
54 | def net_br(self):
55 | self.net_br_gw()
56 |
57 | def net_noext(self):
58 | self.netcall('ip link set dev eth0 down')
59 | self.netcall('ip route replace unreachable {dns}/32')
60 |
61 | def net_br_gw(self):
62 | self.call('sudo {pipework} br-{br} -i {intif} -l {vethif} {name} {ip4}/24@{gw4}')
63 |
64 | def net_br_nogw(self):
65 | self.call('sudo {pipework} br-{br} -i {intif} -l {vethif} {name} {ip4}/24')
66 |
67 | @property
68 | def br(self):
69 | return BR[self.ip4.rsplit('.', 1)[0]]
70 | @property
71 | def gw4(self):
72 | return GW[self.br]
73 | @property
74 | def intif(self):
75 | return {'web': 'ethw', 'tank': 'etht', 'txrx': 'ethx'}[self.br]
76 | @property
77 | def vethif(self):
78 | return ('v' + self.intif + self.name)[:15] # IFNAMSIZ 16
79 |
80 | def assert_good_death(self):
81 | pass
82 |
83 | def close(self):
84 | if hasattr(self, 'sha'):
85 | self.call('sudo docker stop --time 1 {sha}')
86 | self.assert_good_death()
87 | if not getattr(self, 'preserve_root', False):
88 | self.call('sudo docker rm {sha}')
89 | del self.sha
90 | def fmt(self, cmd):
91 | ctx = self.__dict__.copy()
92 | for i in xrange(len(dir(self))):
93 | try:
94 | ret = cmd.format(**ctx).split()
95 | break
96 | except KeyError, e:
97 | key = e.args[0]
98 | ctx[key] = getattr(self, key)
99 | return ret
100 | def output(self, cmd, **kwargs):
101 | return check_output(self.fmt(cmd), **kwargs)
102 | def logs(self, since=None):
103 | out = self.output('sudo docker logs {sha}', stderr=STDOUT)
104 | out = [_.split(None, 1) for _ in out.split('\n')]
105 | out = [(float(_[0]), _[1]) for _ in out if len(_) == 2 and _[0][:10].isdigit()]
106 | if since is not None:
107 | out = [_ for _ in out if _[0] >= since]
108 | return out
109 | def call(self, cmd):
110 | check_call(self.fmt(cmd))
111 | def do(self, cmd):
112 | return self.output('sudo docker exec {sha} ' + cmd)
113 | def netcall(self, cmd):
114 | return self.output('sudo ip netns exec {name} ' + cmd)
115 | def lsfd(self):
116 | out = self.output('sudo lsof -p {pid} -F f')
117 | out = [int(_[1:]) for _ in out.split() if _[0] == 'f' and _[1:].isdigit()]
118 | assert len(out) > 0
119 | return out
120 |
121 | class WebVM(VM):
122 | def __init__(self):
123 | VM.__init__(self, 'web', 'redsocks/web', '10.0.1.80')
124 |
125 | class InetdVM(VM):
126 | def __init__(self):
127 | VM.__init__(self, 'inetd', 'redsocks/inetd', '10.0.1.13')
128 |
129 | class SquidVM(VM):
130 | def __init__(self, no):
131 | VM.__init__(self, 'squid-%d' % no, 'redsocks/squid', '10.0.1.%d' % no,
132 | docker_opt='--ulimit nofile=65535:65535',
133 | cmd='/etc/squid3/squid-%d.conf' % no)
134 | def net(self):
135 | self.net_br_nogw()
136 | self.netcall('ip route replace 10.0.0.0/16 via 10.0.1.1')
137 |
138 | class DanteVM(VM):
139 | def __init__(self, no):
140 | VM.__init__(self, 'dante-%d' % no, 'redsocks/dante', '10.0.1.%d' % (180 + no),
141 | cmd='/etc/danted-%d.conf' % (1080 + no))
142 | def net(self):
143 | self.net_br_nogw()
144 | self.netcall('ip route replace 10.0.0.0/16 via 10.0.1.1')
145 |
146 |
147 | class GwVM(VM):
148 | def __init__(self):
149 | VM.__init__(self, 'gw', 'ubuntu:14.04', cmd=SLEEPING_BEAST)
150 | def net_br(self):
151 | self.ip4 = '10.0.1.1'
152 | self.net_br_nogw()
153 | self.ip4 = '10.0.8.1'
154 | self.net_br_nogw()
155 | del self.ip4
156 | self.netcall('ip route replace unreachable 10.0.2.0/24')
157 | self.netcall('iptables -A FORWARD --destination 10.0.1.66/32 -j DROP')
158 |
159 | class TankVM(VM):
160 | def __init__(self, no):
161 | assert 1 <= no <= 100
162 | VM.__init__(self, 'tank%d' % no, 'redsocks/tank', '10.0.2.%d' % no, cmd=SLEEPING_BEAST)
163 |
164 | class RegwVM(VM):
165 | def __init__(self):
166 | kw = {}
167 | self.debug = os.environ.get('DEBUG_TEST', '')
168 | if self.debug:
169 | self.preserve_root = True
170 | if self.debug != 'logs':
171 | kw['cmd'] = {
172 | 'valgrind': 'valgrind --leak-check=full --show-leak-kinds=all /usr/local/sbin/redsocks -c /usr/local/etc/redsocks.conf',
173 | 'strace': 'strace -ttt /usr/local/sbin/redsocks -c /usr/local/etc/redsocks.conf',
174 | }[self.debug]
175 | VM.__init__(self, 'regw', 'redsocks/regw', **kw)
176 | def net_br(self):
177 | self.ip4 = '10.0.2.123'
178 | self.net_br_nogw()
179 | self.ip4 = '10.0.8.123'
180 | self.net_br_gw()
181 | del self.ip4
182 | for t in TANKS.values():
183 | self.netcall('iptables -t nat -A PREROUTING --source 10.0.2.%d/32 --dest 10.0.1.0/24 -p tcp -j REDIRECT --to-port %d' % (t, 12340 + t - TANKS_BASE))
184 | def assert_good_death(self):
185 | out = self.output('sudo docker logs {sha}', stderr=STDOUT)
186 | assert 'redsocks goes down' in out and 'There are connected clients during shutdown' not in out
187 | assert self.debug != 'valgrind' or 'no leaks are possible' in out
188 |
189 | CPU = object()
190 | MAX = object()
191 | def pmap(l, j=MAX):
192 | #return map(lambda x: x(), l)
193 | if j is MAX:
194 | j = len(l)
195 | elif j is CPU:
196 | j = multiprocessing.cpu_count()
197 | p = ThrPool(j)
198 | try:
199 | return p.map(lambda x: x(), l, chunksize=1)
200 | finally:
201 | p.close()
202 | p.join()
203 |
204 | TANKS_BASE = 10
205 | TANKS = {
206 | 'connect_none': TANKS_BASE + 0,
207 | 'connect_basic': TANKS_BASE + 1,
208 | 'connect_digest': TANKS_BASE + 2,
209 | 'socks5_none': TANKS_BASE + 3,
210 | 'socks5_auth': TANKS_BASE + 4,
211 | 'connect_nopass': TANKS_BASE + 5,
212 | 'connect_baduser': TANKS_BASE + 6,
213 | 'connect_badpass': TANKS_BASE + 7,
214 | 'socks5_nopass': TANKS_BASE + 8,
215 | 'socks5_baduser': TANKS_BASE + 9,
216 | 'socks5_badpass': TANKS_BASE + 10,
217 | 'httperr_connect_nopass': TANKS_BASE + 11,
218 | 'httperr_connect_baduser': TANKS_BASE + 12,
219 | 'httperr_connect_badpass': TANKS_BASE + 13,
220 | 'httperr_connect_digest': TANKS_BASE + 14,
221 | 'httperr_proxy_refuse': TANKS_BASE + 15,
222 | 'httperr_proxy_timeout': TANKS_BASE + 16,
223 | }
224 |
225 | class _Network(object):
226 | def __init__(self):
227 | assert os.path.exists(PIPEWORK_CMD)
228 | running = check_output('sudo docker ps -q'.split()).split()
229 | assert running == []
230 | names = check_output('sudo docker ps -a --format {{.Names}}'.split()).split()
231 | assert 'regw' not in names
232 | vm = [
233 | GwVM,
234 | WebVM,
235 | InetdVM,
236 | RegwVM,
237 | partial(SquidVM, 8),
238 | partial(SquidVM, 9),
239 | partial(DanteVM, 0),
240 | partial(DanteVM, 1),
241 | ]
242 | for t in TANKS.values():
243 | vm.append(partial(TankVM, t))
244 | self.vm = {_.name: _ for _ in pmap(vm, j=CPU)} # pmap saves ~5 seconds
245 | def close(self):
246 | check_output('sudo docker ps'.split())
247 | pmap([_.close for _ in self.vm.values()]) # pmap saves ~7 seconds
248 |
249 | @pytest.fixture(scope="session")
250 | def net(request):
251 | n = _Network()
252 | request.addfinalizer(n.close)
253 | return n
254 |
255 | def pytest_addoption(parser):
256 | parser.addoption('--vmdebug', action='store_true', help='run `test_debug` test')
257 |
258 | def pytest_cmdline_preparse(args):
259 | if '--vmdebug' in args:
260 | args[:] = ['-k', 'test_vmdebug'] + args
261 |
--------------------------------------------------------------------------------
/tests/dante/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 |
3 | RUN set -o xtrace \
4 | && sed -i 's,^deb-src,# no src # &,; s,http://archive.ubuntu.com/ubuntu/,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list \
5 | && apt-get update \
6 | && apt-get install -y dante-server
7 |
8 | RUN useradd sockusr --user-group \
9 | --home-dir /nonexistent --no-create-home \
10 | --shell /usr/sbin/nologin \
11 | --password '$6$U1HPxoVq$XFqhRetreV3068UCwQA//fGVFUfwfyqeiYpCpeUFAhuMi/wjOhJSzUxb4wUqt9vRnWjO0CDPMkE40ptHWrrIz.'
12 |
13 | COPY danted-*.conf /etc/
14 | COPY danted-waitif /usr/sbin/
15 |
16 | ENTRYPOINT ["/usr/sbin/danted-waitif", "-d", "-f"]
17 | CMD ["/etc/danted-1080.conf"]
18 |
--------------------------------------------------------------------------------
/tests/dante/danted-1080.conf:
--------------------------------------------------------------------------------
1 | logoutput: stderr
2 | internal: 0.0.0.0 port = 1080
3 | external: ethw
4 | method: none
5 | clientmethod: none
6 |
7 | #extension: bind
8 |
9 | # TCP addr rules
10 |
11 | client pass {
12 | from: 10.0.8.123/32 port 1-65535 to: 0.0.0.0/0
13 | log: connect error
14 | }
15 | client block {
16 | from: 0.0.0.0/0 to: 0.0.0.0/0
17 | log: connect error
18 | }
19 |
20 | # Socks protocol rules
21 |
22 | block {
23 | from: 0.0.0.0/0 to: 127.0.0.0/8
24 | log: connect error
25 | }
26 | pass {
27 | from: 10.0.8.123/32 to: 0.0.0.0/0
28 | log: connect error
29 | }
30 | block {
31 | from: 0.0.0.0/0 to: 0.0.0.0/0
32 | log: connect error
33 | }
34 |
--------------------------------------------------------------------------------
/tests/dante/danted-1081.conf:
--------------------------------------------------------------------------------
1 | logoutput: stderr
2 | internal: 0.0.0.0 port = 1081
3 | external: ethw
4 | method: username
5 | clientmethod: none
6 |
7 | #extension: bind
8 |
9 | # TCP addr rules
10 |
11 | client pass {
12 | from: 10.0.8.123/32 port 1-65535 to: 0.0.0.0/0
13 | log: connect error
14 | }
15 | client block {
16 | from: 0.0.0.0/0 to: 0.0.0.0/0
17 | log: connect error
18 | }
19 |
20 | # Socks protocol rules
21 |
22 | block {
23 | from: 0.0.0.0/0 to: 127.0.0.0/8
24 | log: connect error
25 | }
26 | pass {
27 | from: 10.0.8.123/32 to: 0.0.0.0/0
28 | log: connect error
29 | }
30 | block {
31 | from: 0.0.0.0/0 to: 0.0.0.0/0
32 | log: connect error
33 | }
34 |
--------------------------------------------------------------------------------
/tests/dante/danted-waitif:
--------------------------------------------------------------------------------
1 | #!/bin/sh -x
2 | while ! ip link show ethw up 2>/dev/null | grep -q UP; do
3 | sleep 0.1
4 | done
5 | exec /usr/sbin/danted "$@"
6 |
--------------------------------------------------------------------------------
/tests/inetd/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 |
3 | RUN set -o xtrace \
4 | && sed -i 's,^deb-src,# no src # &,; s,http://archive.ubuntu.com/ubuntu/,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list \
5 | && apt-get update \
6 | && apt-get install -y xinetd
7 |
8 | RUN set -o xtrace \
9 | && apt-get install -y iperf
10 |
11 | COPY testing /etc/xinetd.d/testing
12 |
13 | CMD ["/usr/sbin/xinetd", "-dontfork", "-pidfile", "/run/xinetd.pid", "-inetd_ipv6"]
14 |
--------------------------------------------------------------------------------
/tests/inetd/testing:
--------------------------------------------------------------------------------
1 | service daytime
2 | {
3 | disable = no
4 | type = INTERNAL
5 | id = daytime-stream
6 | socket_type = stream
7 | protocol = tcp
8 | user = root
9 | wait = no
10 | flags = IPv6
11 | }
12 |
13 | service discard
14 | {
15 | disable = no
16 | type = INTERNAL
17 | id = discard-stream
18 | socket_type = stream
19 | protocol = tcp
20 | user = root
21 | wait = no
22 | flags = IPv6
23 | }
24 |
25 | service echo
26 | {
27 | disable = no
28 | type = INTERNAL
29 | id = echo-stream
30 | socket_type = stream
31 | protocol = tcp
32 | user = root
33 | wait = no
34 | flags = IPv6
35 | }
36 |
37 | service chargen
38 | {
39 | disable = no
40 | type = INTERNAL
41 | id = chargen-stream
42 | socket_type = stream
43 | protocol = tcp
44 | user = root
45 | wait = no
46 | flags = IPv6
47 | }
48 |
--------------------------------------------------------------------------------
/tests/login:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | vm=$1
3 | shift
4 | if [ $# = 0 ]; then
5 | set -- /bin/bash
6 | fi
7 | if [ -t 0 -a -t 1 -a -t 2 ]; then
8 | tty="--tty"
9 | else
10 | tty=""
11 | fi
12 | set -x
13 | sudo docker exec --interactive $tty "$vm" "$@"
14 |
--------------------------------------------------------------------------------
/tests/prlimit-nofile.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | // prlimit(1) is util-linux 2.21+ and I'm stuck with 2.20 at the moment
7 |
8 | int main(int argc, char *argv[])
9 | {
10 | if (argc != 3)
11 | errx(EXIT_FAILURE, "Usage: %s ", argv[0]);
12 |
13 | pid_t pid = atoi(argv[1]);
14 | rlim_t soft = atoi(argv[2]);
15 |
16 | struct rlimit rl;
17 | if (prlimit(pid, RLIMIT_NOFILE, NULL, &rl) == -1)
18 | err(EXIT_FAILURE, "prlimit(%d, RLIMIT_NOFILE, NULL, %p)", pid, &rl);
19 |
20 | if (rl.rlim_max < soft)
21 | errx(EXIT_FAILURE, "rlim_max = %ld, requested limit = %ld", rl.rlim_max, soft);
22 |
23 | rl.rlim_cur = soft;
24 |
25 | if (prlimit(pid, RLIMIT_NOFILE, &rl, NULL) == -1)
26 | err(EXIT_FAILURE, "prlimit(%d, RLIMIT_NOFILE, %p, NULL)", pid, &rl);
27 |
28 | return 0;
29 | }
30 |
--------------------------------------------------------------------------------
/tests/regw/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 |
3 | RUN set -o xtrace \
4 | && sed -i 's,^deb-src,# no src # &,; s,http://archive.ubuntu.com/ubuntu/,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list \
5 | && apt-get update \
6 | && apt-get install -y libevent-2.0-5 valgrind curl strace
7 |
8 | COPY redsocks /usr/local/sbin/
9 | COPY redsocks.conf /usr/local/etc/
10 | CMD ["/usr/local/sbin/redsocks", "-c", "/usr/local/etc/redsocks.conf"]
11 |
--------------------------------------------------------------------------------
/tests/regw/redsocks.conf:
--------------------------------------------------------------------------------
1 | base {
2 | log_debug = on;
3 | log_info = on;
4 | log = stderr;
5 | daemon = off;
6 | redirector = iptables;
7 | redsocks_conn_max = 8;
8 | }
9 |
10 | redsocks {
11 | local_ip = 0.0.0.0;
12 | local_port = 12340;
13 | ip = 10.0.1.8;
14 | port = 8080;
15 | type = http-connect;
16 | }
17 |
18 | redsocks {
19 | local_ip = 0.0.0.0;
20 | local_port = 12341;
21 | ip = 10.0.1.8;
22 | port = 8081;
23 | type = http-connect;
24 | login = basic_user;
25 | password = basic_password;
26 | }
27 |
28 | redsocks {
29 | local_ip = 0.0.0.0;
30 | local_port = 12342;
31 | ip = 10.0.1.9;
32 | port = 8081;
33 | type = http-connect;
34 | login = digest_user;
35 | password = digest_password;
36 | }
37 |
38 | redsocks {
39 | local_ip = 0.0.0.0;
40 | local_port = 12343;
41 | ip = 10.0.1.180;
42 | port = 1080;
43 | type = socks5;
44 | }
45 |
46 | redsocks {
47 | local_ip = 0.0.0.0;
48 | local_port = 12344;
49 | ip = 10.0.1.181;
50 | port = 1081;
51 | type = socks5;
52 | login = sockusr;
53 | password = sockpwd;
54 | }
55 |
56 | redsocks {
57 | local_ip = 0.0.0.0;
58 | local_port = 12345;
59 | ip = 10.0.1.9;
60 | port = 8081;
61 | type = http-connect;
62 | }
63 |
64 | redsocks {
65 | local_ip = 0.0.0.0;
66 | local_port = 12346;
67 | ip = 10.0.1.9;
68 | port = 8081;
69 | type = http-connect;
70 | login = luser;
71 | password = digest_password;
72 | }
73 |
74 | redsocks {
75 | local_ip = 0.0.0.0;
76 | local_port = 12347;
77 | ip = 10.0.1.9;
78 | port = 8081;
79 | type = http-connect;
80 | login = digest_user;
81 | password = buzzword;
82 | }
83 |
84 | redsocks {
85 | local_ip = 0.0.0.0;
86 | local_port = 12348;
87 | ip = 10.0.1.181;
88 | port = 1081;
89 | type = socks5;
90 | }
91 |
92 | redsocks {
93 | local_ip = 0.0.0.0;
94 | local_port = 12349;
95 | ip = 10.0.1.181;
96 | port = 1081;
97 | type = socks5;
98 | login = luser;
99 | password = sockpwd;
100 | }
101 |
102 | redsocks {
103 | local_ip = 0.0.0.0;
104 | local_port = 12350;
105 | ip = 10.0.1.181;
106 | port = 1081;
107 | type = socks5;
108 | login = sockusr;
109 | password = buzzword;
110 | }
111 |
112 | redsocks {
113 | local_ip = 0.0.0.0;
114 | local_port = 12351;
115 | ip = 10.0.1.9;
116 | port = 8081;
117 | type = http-connect;
118 | on_proxy_fail = forward_http_err;
119 | }
120 |
121 | redsocks {
122 | local_ip = 0.0.0.0;
123 | local_port = 12352;
124 | ip = 10.0.1.9;
125 | port = 8081;
126 | type = http-connect;
127 | login = luser;
128 | password = digest_password;
129 | on_proxy_fail = forward_http_err;
130 | }
131 |
132 | redsocks {
133 | local_ip = 0.0.0.0;
134 | local_port = 12353;
135 | ip = 10.0.1.9;
136 | port = 8081;
137 | type = http-connect;
138 | login = digest_user;
139 | password = buzzword;
140 | on_proxy_fail = forward_http_err;
141 | }
142 |
143 | redsocks {
144 | local_ip = 0.0.0.0;
145 | local_port = 12354;
146 | ip = 10.0.1.9;
147 | port = 8081;
148 | type = http-connect;
149 | login = digest_user;
150 | password = digest_password;
151 | on_proxy_fail = forward_http_err;
152 | }
153 |
154 | redsocks {
155 | local_ip = 0.0.0.0;
156 | local_port = 12355;
157 | ip = 10.0.1.9;
158 | port = 1;
159 | type = http-connect;
160 | on_proxy_fail = forward_http_err;
161 | }
162 |
163 | redsocks {
164 | local_ip = 0.0.0.0;
165 | local_port = 12356;
166 | ip = 10.0.1.66;
167 | port = 66;
168 | type = http-connect;
169 | on_proxy_fail = forward_http_err;
170 | }
171 |
--------------------------------------------------------------------------------
/tests/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | exec py.test -v "$@"
4 |
--------------------------------------------------------------------------------
/tests/squid/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 |
3 | RUN set -o xtrace \
4 | && sed -i 's,^deb-src,# no src # &,; s,http://archive.ubuntu.com/ubuntu/,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list \
5 | && apt-get update \
6 | && apt-get install -y squid3
7 |
8 | COPY squid-?.conf basic.passwd digest.passwd /etc/squid3/
9 |
10 | # that's from /etc/init/squid3.conf
11 | ENTRYPOINT ["/usr/sbin/squid3", "-NYC", "-f"]
12 | CMD ["/etc/squid3/squid-8.conf"]
13 |
--------------------------------------------------------------------------------
/tests/squid/basic.passwd:
--------------------------------------------------------------------------------
1 | basic_user:$apr1$bslox4us$3RXJauJbX8riqzVzzOYc70
2 |
--------------------------------------------------------------------------------
/tests/squid/digest.passwd:
--------------------------------------------------------------------------------
1 | digest_user:digest_password
2 |
--------------------------------------------------------------------------------
/tests/squid/squid-8.conf:
--------------------------------------------------------------------------------
1 | auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid3/basic.passwd
2 | auth_param basic children 5 startup=0 idle=1
3 | auth_param basic realm Squid proxy-caching web server
4 | auth_param basic credentialsttl 2 hours
5 |
6 | acl good proxy_auth basic_user
7 |
8 | # same conf after the line
9 |
10 | acl portopen localport 8080
11 | acl portauth localport 8081
12 |
13 | acl regw src 10.0.8.123/32
14 |
15 | http_access deny manager
16 | http_access deny to_localhost
17 | http_access allow regw portopen
18 | http_access allow regw portauth good
19 | http_access deny all
20 |
21 | http_port 0.0.0.0:8080
22 | http_port 0.0.0.0:8081
23 |
24 | #http_port 0.0.0.0:8082
25 |
26 | #ssl_bump splice all
27 | # http_port [::]:8080
28 |
29 | #http_port 127.0.0.1:12345 intercept
30 | #http_port [::1]:12345 intercept
31 |
32 | #https_port 127.0.0.1:12346 intercept ssl-bump cert=/etc/ssl/certs/ssl-cert-snakeoil.pem key=/etc/ssl/private/ssl-cert-snakeoil.key
33 | #https_port [::1]:12346 intercept ssl-bump cert=/etc/ssl/certs/ssl-cert-snakeoil.pem key=/etc/ssl/private/ssl-cert-snakeoil.key
34 |
35 | #cache_peer 80.80.80.80 parent 8080 0 no-digest no-netdb-exchange proxy-only
36 | #cache_peer_access 80.80.80.80 allow all
37 | #never_direct allow all
38 |
39 | forwarded_for delete
40 | via off
41 | reply_header_access X-Cache-Lookup deny all
42 | reply_header_access X-Squid-Error deny all
43 | reply_header_access X-Cache deny all
44 | request_header_access Cache-Control deny all
45 | cache deny all
46 | memory_pools off
47 |
--------------------------------------------------------------------------------
/tests/squid/squid-9.conf:
--------------------------------------------------------------------------------
1 | auth_param digest program /usr/lib/squid3/digest_file_auth /etc/squid3/digest.passwd
2 | auth_param digest children 5 startup=0 idle=1
3 | auth_param digest realm Squid proxy-caching web server
4 | auth_param digest nonce_garbage_interval 5 minutes
5 | auth_param digest nonce_max_duration 30 minutes
6 | auth_param digest nonce_max_count 50
7 |
8 | acl good proxy_auth digest_user
9 |
10 | # same conf after the line
11 |
12 | acl portopen localport 8080
13 | acl portauth localport 8081
14 |
15 | acl regw src 10.0.8.123/32
16 |
17 | http_access deny manager
18 | http_access deny to_localhost
19 | http_access allow regw portopen
20 | http_access allow regw portauth good
21 | http_access deny all
22 |
23 | http_port 0.0.0.0:8080
24 | http_port 0.0.0.0:8081
25 |
26 | #http_port 0.0.0.0:8082
27 |
28 | #ssl_bump splice all
29 | # http_port [::]:8080
30 |
31 | #http_port 127.0.0.1:12345 intercept
32 | #http_port [::1]:12345 intercept
33 |
34 | #https_port 127.0.0.1:12346 intercept ssl-bump cert=/etc/ssl/certs/ssl-cert-snakeoil.pem key=/etc/ssl/private/ssl-cert-snakeoil.key
35 | #https_port [::1]:12346 intercept ssl-bump cert=/etc/ssl/certs/ssl-cert-snakeoil.pem key=/etc/ssl/private/ssl-cert-snakeoil.key
36 |
37 | #cache_peer 80.80.80.80 parent 8080 0 no-digest no-netdb-exchange proxy-only
38 | #cache_peer_access 80.80.80.80 allow all
39 | #never_direct allow all
40 |
41 | forwarded_for delete
42 | via off
43 | reply_header_access X-Cache-Lookup deny all
44 | reply_header_access X-Squid-Error deny all
45 | reply_header_access X-Cache deny all
46 | request_header_access Cache-Control deny all
47 | cache deny all
48 | memory_pools off
49 |
--------------------------------------------------------------------------------
/tests/tank/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 |
3 | RUN set -o xtrace \
4 | && sed -i 's,^deb-src,# no src # &,; s,http://archive.ubuntu.com/ubuntu/,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list \
5 | && apt-get update \
6 | && apt-get install -y apache2-utils curl pv python-tornado
7 |
8 | RUN set -o xtrace \
9 | && apt-get install -y iperf
10 |
11 | COPY benchmark.py /benchmark.py
12 |
13 | # yandex-tank is nice `ab' replacement, but it's PPA is broken at the moment
14 | # https://github.com/yandex/yandex-tank/issues/202
15 | #
16 | # apt-get install -y software-properties-common
17 | # add-apt-repository ppa:yandex-load/main
18 | # apt-get install -y phantom phantom-ssl yandex-tank
19 |
--------------------------------------------------------------------------------
/tests/tank/benchmark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python2
2 |
3 | import argparse
4 | import functools
5 | import socket
6 | import logging
7 | import json
8 | import time
9 | import datetime
10 | import sys
11 |
12 | import tornado.ioloop as ioloop
13 | import tornado.iostream as iostream
14 | import tornado.httpclient as httpclient
15 |
16 | log = logging.getLogger('benchmark')
17 |
18 | conlist = []
19 | stats = []
20 |
21 | DELAY = 0.1
22 |
23 | class Mode(object):
24 | def __init__(self, opt):
25 | self.opt = opt
26 | conlist.append(self)
27 | log.debug('%d of %d', len(conlist), opt.stop)
28 | self.start()
29 | def grab_stats(self):
30 | if self.opt.json is not None:
31 | client = httpclient.AsyncHTTPClient()
32 | self.begin_stats = time.time()
33 | client.fetch('http://%s/debug/meminfo.json' % self.opt.json, self.parse_stats)
34 | else:
35 | self.schedule_next()
36 | def parse_stats(self, response):
37 | log.debug('got stats')
38 | self.end_stats = time.time()
39 | sdict = json.loads(response.body)
40 | sdict['begin'] = self.begin_stats
41 | sdict['end'] = self.end_stats
42 | sdict['conns'] = len(conlist)
43 | stats.append(sdict)
44 | self.schedule_next()
45 | def schedule_next(self):
46 | if len(conlist) < self.opt.stop:
47 | ioloop.IOLoop.current().add_timeout(datetime.timedelta(seconds=DELAY), functools.partial(self.__class__, self.opt))
48 | else:
49 | ioloop.IOLoop.current().stop()
50 |
51 | class Pipe(Mode):
52 | def start(self):
53 | client = httpclient.AsyncHTTPClient()
54 | self.begin_stats = time.time()
55 | client.fetch('http://%s/debug/pipe' % self.opt.json, self.on_pipe)
56 | def on_pipe(self, response):
57 | self.grab_stats()
58 |
59 | class Connector(Mode):
60 | def start(self):
61 | fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
62 | self.stream = iostream.IOStream(fd)
63 | self.stream.connect((opt.host, opt.port), self.on_connect)
64 |
65 | class EchoConnector(Connector):
66 | ECHO = 'ECHO\x0d\x0a'
67 | def on_connect(self):
68 | self.stream.write(self.ECHO)
69 | self.stream.read_bytes(len(self.ECHO), self.on_pong)
70 | def on_pong(self, data):
71 | assert data == self.ECHO
72 | self.grab_stats()
73 |
74 | class ChargenConnector(Connector):
75 | def __init__(self, opt):
76 | Connector.__init__(self, opt)
77 | self.gotstats = False
78 | def on_connect(self):
79 | self.stream.read_until_close(self.on_close, self.on_datachunk)
80 | def on_close(self, data):
81 | pass
82 | def on_datachunk(self, data):
83 | if not self.gotstats:
84 | self.gotstats = True
85 | self.grab_stats()
86 |
87 | MODES = {
88 | 'echo': EchoConnector,
89 | 'chargen': ChargenConnector,
90 | 'pipe': Pipe,
91 | }
92 |
93 | def parse_cmd(argv):
94 | parser = argparse.ArgumentParser(prog=argv[0], description='/ping handler')
95 | parser.add_argument('--json', type=str)
96 | parser.add_argument('--stop', default=10, type=int)
97 | parser.add_argument('--verbose', default=False, action='store_true')
98 | parser.add_argument('--mode', metavar='MODE', required=True, choices=sorted(MODES))
99 | parser.add_argument('--port', metavar='PORT', type=int, default=None)
100 | parser.add_argument('host', metavar='HOST', type=str)
101 | if len(sys.argv) == 1:
102 | sys.argv.append('-h')
103 | opt = parser.parse_args(argv[1:])
104 | if opt.port is None:
105 | opt.port = {'echo': 7, 'chargen': 19, 'pipe': -1}[opt.mode]
106 | return opt
107 |
108 | def main(opt):
109 | LOGGING_FORMAT = '%(asctime)s %(name)s[%(process)d] %(levelname)s: %(message)s'
110 | if not opt.verbose:
111 | logging.basicConfig(level=logging.INFO, format=LOGGING_FORMAT)
112 | logging.getLogger('tornado.access').setLevel(logging.INFO+1)
113 | else:
114 | logging.basicConfig(level=logging.DEBUG, format=LOGGING_FORMAT)
115 |
116 | klass = MODES[opt.mode]
117 | klass(opt)
118 |
119 | try:
120 | ioloop.IOLoop.instance().start()
121 | except KeyboardInterrupt:
122 | pass
123 | finally:
124 | log.fatal('Done.')
125 | print json.dumps(stats)
126 |
127 | if __name__ == '__main__':
128 | main(parse_cmd(sys.argv))
129 |
--------------------------------------------------------------------------------
/tests/test_smoke.py:
--------------------------------------------------------------------------------
1 | from functools import partial
2 | from subprocess import check_call, CalledProcessError, Popen, PIPE
3 | import time
4 |
5 | import conftest
6 | import pytest
7 |
8 | @pytest.mark.skipif(not pytest.config.getoption('--vmdebug'), reason='need --vmdebug option to run')
9 | def test_vmdebug(net):
10 | check_call('sleep 365d'.split())
11 |
12 | GOOD_AUTH = 'connect_none connect_basic connect_digest socks5_none socks5_auth httperr_connect_digest'.split()
13 | BAD_AUTH = 'connect_nopass connect_baduser connect_badpass socks5_nopass socks5_baduser socks5_badpass httperr_proxy_refuse'.split()
14 | UGLY_AUTH = 'httperr_connect_nopass httperr_connect_baduser httperr_connect_badpass'.split()
15 | assert set(conftest.TANKS) == set(GOOD_AUTH + BAD_AUTH + UGLY_AUTH + ['httperr_proxy_timeout'])
16 |
17 | @pytest.mark.parametrize('tank', GOOD_AUTH)
18 | def test_smoke(net, tank):
19 | vm = net.vm['tank%d' % conftest.TANKS[tank]]
20 | page = vm.do('curl --max-time 0.5 http://10.0.1.80/')
21 | assert 'Welcome to nginx!' in page
22 |
23 | @pytest.mark.parametrize('tank', BAD_AUTH)
24 | def test_badauth(net, tank):
25 | vm = net.vm['tank%d' % conftest.TANKS[tank]]
26 | with pytest.raises(CalledProcessError) as excinfo:
27 | vm.do('curl --max-time 0.5 http://10.0.1.80/')
28 | assert excinfo.value.returncode == 52 # Empty reply from server
29 |
30 | @pytest.mark.parametrize('tank', UGLY_AUTH)
31 | def test_uglyauth(net, tank):
32 | vm = net.vm['tank%d' % conftest.TANKS[tank]]
33 | page = vm.do('curl -sSv --max-time 0.5 http://10.0.1.80/')
34 | assert '' in page
35 |
36 | @pytest.mark.parametrize('tank', set(conftest.TANKS) - set(UGLY_AUTH + ['httperr_connect_digest']))
37 | def test_econnrefused(net, tank):
38 | vm = net.vm['tank%d' % conftest.TANKS[tank]]
39 | with pytest.raises(CalledProcessError) as excinfo:
40 | vm.do('curl --max-time 0.5 http://10.0.1.80:81/')
41 | if tank == 'httperr_proxy_timeout':
42 | assert excinfo.value.returncode == 28 # Operation timed out
43 | else:
44 | assert excinfo.value.returncode == 52 # Empty reply from server
45 |
46 | def test_econnrefused_httperr(net):
47 | tank = 'httperr_connect_digest'
48 | vm = net.vm['tank%d' % conftest.TANKS[tank]]
49 | page = vm.do('curl --max-time 0.5 http://10.0.1.80:81/')
50 | assert '' in page
51 |
52 | RTT = 200 # ms
53 |
54 | @pytest.fixture(scope="function")
55 | def slow_net(request, net):
56 | def close():
57 | net.vm['gw'].netcall('tc qdisc del dev ethx root')
58 | net.vm['gw'].netcall('tc qdisc del dev ethw root')
59 | request.addfinalizer(close)
60 | net.vm['gw'].netcall('tc qdisc add dev ethw root netem delay %dms' % (RTT / 2))
61 | net.vm['gw'].netcall('tc qdisc add dev ethx root netem delay %dms' % (RTT / 2))
62 | return net
63 |
64 | LATENCY = {
65 | 'connect_none': 3 * RTT,
66 | 'connect_basic': 3 * RTT,
67 | 'connect_digest': 3 * RTT,
68 | 'socks5_none': 4 * RTT,
69 | 'socks5_auth': 5 * RTT,
70 | 'regw_direct': 2 * RTT,
71 | }
72 |
73 | def heatup(vm):
74 | vm.do('curl -o /dev/null http://10.0.1.80/') # heatup L2 and auth caches
75 |
76 | def http_ping(vm):
77 | s = vm.do('curl -sS -w %{{time_connect}}/%{{time_total}}/%{{http_code}}/%{{size_download}} -o /dev/null http://10.0.1.80/')
78 | connect, total, code, size = s.split('/')
79 | connect, total, code, size = float(connect) * 1000, float(total) * 1000, int(code), int(size)
80 | return connect, total, code, size
81 |
82 | @pytest.mark.parametrize('tank', set(conftest.TANKS) & set(LATENCY))
83 | def test_latency_tank(slow_net, tank):
84 | vm = slow_net.vm['tank%d' % conftest.TANKS[tank]]
85 | heatup(vm)
86 | connect, total, code, size = http_ping(vm)
87 | assert code == 200 and size == 612
88 | assert connect < 0.005 and LATENCY[tank]-RTT*.2 < total and total < LATENCY[tank]+RTT*.2
89 |
90 | def test_latency_regw(slow_net):
91 | vm, tank = slow_net.vm['regw'], 'regw_direct'
92 | heatup(vm)
93 | connect, total, code, size = http_ping(vm)
94 | assert code == 200 and size == 612
95 | assert RTT*.8 < connect and connect < RTT*1.2 and LATENCY[tank]-RTT*.2 < total and total < LATENCY[tank]+RTT*.2
96 |
97 | def test_nonce_reuse(slow_net):
98 | """ nonce reuse works and has no latency penalty """
99 | tank = 'connect_digest'
100 | vm = slow_net.vm['tank%d' % conftest.TANKS[tank]]
101 | heatup(vm)
102 | begin = time.time()
103 | s = conftest.pmap([partial(http_ping, vm) for _ in range(5)])
104 | total_sum = time.time() - begin
105 | for connect, total, code, size in s:
106 | assert code == 200 and size == 612
107 | assert connect < 0.005 and LATENCY[tank]-RTT*.2 < total and total < LATENCY[tank]+RTT*.2
108 | assert total_sum < total * 1.5
109 |
110 | @pytest.mark.parametrize('tank, delay', [(t, d)
111 | for t in set(conftest.TANKS) & set(LATENCY)
112 | for d in [_*0.001 for _ in range(1, LATENCY[t]+RTT, RTT/2)]
113 | ] + [
114 | ('httperr_proxy_refuse', (1 + 0*RTT/2) * 0.001),
115 | ('httperr_proxy_refuse', (1 + 1*RTT/2) * 0.001),
116 | ('httperr_proxy_refuse', (1 + 2*RTT/2) * 0.001),
117 | ('httperr_proxy_refuse', (1 + 3*RTT/2) * 0.001),
118 | ('httperr_proxy_timeout', (1 + 3*RTT/2) * 0.001),
119 | ])
120 | def test_impatient_client(slow_net, tank, delay):
121 | vm, regw = slow_net.vm['tank%d' % conftest.TANKS[tank]], slow_net.vm['regw']
122 | before, start = regw.lsfd(), time.time()
123 | try:
124 | page = vm.do('curl --max-time %s http://10.0.1.80/1M' % delay)
125 | #assert 'Welcome to nginx!' in page
126 | except CalledProcessError, e:
127 | if tank == 'httperr_proxy_refuse':
128 | assert e.returncode in (28, 52) # Operation timeout / Empty reply
129 | else:
130 | assert e.returncode == 28 # Operation timeout
131 | curl_time = time.time() - start
132 | assert curl_time < delay + RTT*0.001 # sanity check
133 | time.sleep( (LATENCY.get(tank, delay*1000) + 4*RTT)*0.001 )
134 | if tank == 'httperr_proxy_timeout':
135 | time.sleep(135) # default connect() timeout is long
136 | assert before == regw.lsfd() # no leaks
137 |
138 | @pytest.fixture(scope='function', params=range(80, 60, -1))
139 | def lowfd_net(request, net):
140 | def close():
141 | net.vm['regw'].call('sudo ./prlimit-nofile {pid} 1024')
142 | request.addfinalizer(close)
143 | net.vm['regw'].call('sudo ./prlimit-nofile {pid} %d' % request.param)
144 | return net
145 |
146 | def test_accept_overflow(lowfd_net):
147 | tank = 'connect_none'
148 | vm, regw = lowfd_net.vm['tank%d' % conftest.TANKS[tank]], lowfd_net.vm['regw']
149 | lsfd = regw.lsfd()
150 | year = str(time.gmtime().tm_year)
151 | discard_cmd = vm.fmt('sudo docker exec --interactive {sha} nc 10.0.1.13 discard')
152 | daytime_cmd = vm.fmt('sudo docker exec --interactive {sha} nc 10.0.1.13 daytime')
153 |
154 | dtstart = time.time()
155 | time.sleep(0.5)
156 | proc = [Popen(discard_cmd, stdin=PIPE, stdout=PIPE) for i in xrange(7)]
157 | time.sleep(0.5)
158 | dt = Popen(daytime_cmd, stdin=PIPE, stdout=PIPE)
159 | time.sleep(0.5)
160 | logs = regw.logs(since=dtstart)
161 | if any(['Too many open files' in _[1] for _ in logs]):
162 | # anything may happen, except leaks
163 | proc[0].communicate('/dev/null\x0d\x0a')
164 | dt.communicate('')
165 | time.sleep(1)
166 | for p in proc[1:]:
167 | p.communicate('/dev/null\x0d\x0a')
168 | else:
169 | assert any(['reached redsocks_conn_max limit' in _[1] for _ in logs])
170 | # RLIMIT_NOFILE was not hit, this `daytime` call actually returns data
171 | daytime, _ = dt.communicate('')
172 | assert year in daytime and dt.returncode == 0
173 | time.sleep(0.5)
174 | dtdeath = time.time()
175 | proc.append(Popen(discard_cmd, stdin=PIPE, stdout=PIPE))
176 | time.sleep(0.5)
177 | logs = regw.logs(since=dtdeath)
178 | assert any(['reached redsocks_conn_max limit' in _[1] for _ in logs])
179 | time.sleep(0.5)
180 | dt = Popen(daytime_cmd, stdin=PIPE, stdout=PIPE)
181 | time.sleep(0.5)
182 | assert all([p.poll() is None for p in proc]) and dt.poll() is None # processes are running
183 | out, _ = proc[0].communicate('/dev/null\x0d\x0a')
184 | daytime, _ = dt.communicate('')
185 | assert year in daytime and dt.returncode == 0
186 | time.sleep(1)
187 | for p in proc[1:]:
188 | out, _ = p.communicate('/dev/null\x0d\x0a')
189 | assert out == ''
190 |
191 | time.sleep(1)
192 | assert lsfd == regw.lsfd() # no leaks
193 |
--------------------------------------------------------------------------------
/tests/web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 |
3 | RUN set -o xtrace \
4 | && sed -i 's,^deb-src,# no src # &,; s,http://archive.ubuntu.com/ubuntu/,mirror://mirrors.ubuntu.com/mirrors.txt,' /etc/apt/sources.list \
5 | && apt-get update \
6 | && apt-get install -y nginx-light
7 |
8 | RUN set -o xtrace \
9 | && dd if=/dev/urandom of=/usr/share/nginx/html/128K count=1 bs=128K \
10 | && dd if=/dev/urandom of=/usr/share/nginx/html/1M count=1 bs=1M \
11 | && for i in `seq 16`; do cat /usr/share/nginx/html/1M; done >/usr/share/nginx/html/16M
12 |
13 | CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
14 |
--------------------------------------------------------------------------------
/timer.h:
--------------------------------------------------------------------------------
1 | #ifndef TIMER_H_SAT_DEC_17_46_12_07_2019
2 | #define TIMER_H_SAT_DEC_17_46_12_07_2019
3 |
4 | #define Timercmp(tvp, uvp, cmp) \
5 | (((tvp)->tv_sec == (uvp)->tv_sec) ? \
6 | ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
7 | ((tvp)->tv_sec cmp (uvp)->tv_sec))
8 | #define Timeradd(tvp, uvp, vvp) \
9 | do { \
10 | (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
11 | (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
12 | if ((vvp)->tv_usec >= 1000000) { \
13 | (vvp)->tv_sec++; \
14 | (vvp)->tv_usec -= 1000000; \
15 | } \
16 | } while (0)
17 | #define Timersub(tvp, uvp, vvp) \
18 | do { \
19 | (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
20 | (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
21 | if ((vvp)->tv_usec < 0) { \
22 | (vvp)->tv_sec--; \
23 | (vvp)->tv_usec += 1000000; \
24 | } \
25 | } while (0)
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/tools/git-repack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 | set -o xtrace
5 |
6 | user="darkk"
7 | proj="redsocks"
8 | versions="0.1 0.2 0.3 0.4"
9 |
10 | for file in `python -c "import urllib2, json; print '\n'.join(d['name'] for d in json.load(urllib2.urlopen('https://api.github.com/repos/${user}/${proj}/downloads')))"`; do
11 | touch "$file.uploaded"
12 | done
13 |
14 | token=""
15 |
16 | for ver in $versions; do
17 | for pkg in tar.gz tar.bz2 tar.lzma; do
18 | case $pkg in
19 | tar.gz) sortof=gzip ;;
20 | tar.bz2) sortof=bzip2 ;;
21 | tar.lzma) sortof=lzma ;;
22 | esac
23 |
24 | tag="release-${ver}"
25 | gittar="github-$tag.tar.gz"
26 | gooddir="${proj}-${ver}"
27 | file="${proj}-${ver}.${pkg}"
28 |
29 | if [ -r "${file}.uploaded" ]; then
30 | continue
31 | fi
32 |
33 | rm -rf "${user}-${proj}-"*
34 |
35 | if [ ! -r "$file" ]; then
36 | if [ ! -d "$gooddir" ]; then
37 | if [ ! -f "$gittar" ]; then
38 | wget -O "$gittar" "https://github.com/${user}/${proj}/tarball/${tag}"
39 | fi
40 | tar --extract --file "$gittar"
41 | mv "${user}-${proj}-"* "$gooddir"
42 | fi
43 | tar "--${sortof}" --create --file "$file" "$gooddir"
44 | fi
45 |
46 | if [ -z "$token" ]; then
47 | if [ ! -r git-repack.sh.token ]; then
48 | curl --data '{"scopes": ["public_repo"], "note": "uploader script"}' -u "$user" "https://api.github.com/authorizations" \
49 | | python -c "import sys, json, pipes; print 'token=%s' % pipes.quote(json.load(sys.stdin)['token'])" \
50 | > git-repack.sh.token
51 | fi
52 | . git-repack.sh.token
53 | fi
54 |
55 | . <(python -c "import os, urllib2, json, pipes; print '\n'.join('GITHUB_%s=%s' % (k.replace('-', '_'), pipes.quote(str(v))) for k, v in json.load(urllib2.urlopen('https://api.github.com/repos/${user}/${proj}/downloads?access_token=${token}', data=json.dumps({'name': '${file}', 'size': os.stat('${file}').st_size, 'description': 'Version ${ver}, ${sortof}', 'content-type': 'application/x-gtar-compressed'}))).iteritems())")
56 | curl \
57 | --include \
58 | -F "key=${GITHUB_path}" -F "acl=${GITHUB_acl}" -F "success_action_status=201" \
59 | -F "Filename=${GITHUB_name}" \
60 | -F "AWSAccessKeyId=${GITHUB_accesskeyid}" -F "Policy=${GITHUB_policy}" -F "Signature=${GITHUB_signature}" \
61 | -F "Content-Type=${GITHUB_mime_type}" \
62 | -F "file=@${file}" https://github.s3.amazonaws.com/
63 | echo;
64 | touch "${file}.uploaded"
65 | done
66 | done
67 |
--------------------------------------------------------------------------------
/tsearch.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "tsearch.h"
4 |
5 | static inline int height(struct node *n) { return n ? n->h : 0; }
6 |
7 | static int rot(void **p, struct node *x, int dir /* deeper side */)
8 | {
9 | struct node *y = x->a[dir];
10 | struct node *z = y->a[!dir];
11 | int hx = x->h;
12 | int hz = height(z);
13 | if (hz > height(y->a[dir])) {
14 | /*
15 | * x
16 | * / \ dir z
17 | * A y / \
18 | * / \ --> x y
19 | * z D /| |\
20 | * / \ A B C D
21 | * B C
22 | */
23 | x->a[dir] = z->a[!dir];
24 | y->a[!dir] = z->a[dir];
25 | z->a[!dir] = x;
26 | z->a[dir] = y;
27 | x->h = hz;
28 | y->h = hz;
29 | z->h = hz+1;
30 | } else {
31 | /*
32 | * x y
33 | * / \ / \
34 | * A y --> x D
35 | * / \ / \
36 | * z D A z
37 | */
38 | x->a[dir] = z;
39 | y->a[!dir] = x;
40 | x->h = hz+1;
41 | y->h = hz+2;
42 | z = y;
43 | }
44 | *p = z;
45 | return z->h - hx;
46 | }
47 |
48 | /* balance *p, return 0 if height is unchanged. */
49 | int __tsearch_balance(void **p)
50 | {
51 | struct node *n = *p;
52 | int h0 = height(n->a[0]);
53 | int h1 = height(n->a[1]);
54 | if (h0 - h1 + 1u < 3u) {
55 | int old = n->h;
56 | n->h = h0h - old;
58 | }
59 | return rot(p, n, h0key);
77 | if (!c)
78 | return n;
79 | a[i++] = &n->a[c>0];
80 | n = n->a[c>0];
81 | }
82 | r = malloc(sizeof *r);
83 | if (!r)
84 | return 0;
85 | r->key = key;
86 | r->a[0] = r->a[1] = 0;
87 | r->h = 1;
88 | /* insert new node, rebalance ancestors. */
89 | *a[--i] = r;
90 | while (i && __tsearch_balance(a[--i]));
91 | return r;
92 | }
93 |
--------------------------------------------------------------------------------
/tsearch.h:
--------------------------------------------------------------------------------
1 | #ifndef TSEARCH_H_SAT_DEC_17_46_12_07_2019
2 | #define TSEARCH_H_SAT_DEC_17_46_12_07_2019
3 |
4 | #include
5 | //#include
6 |
7 | /* AVL tree height < 1.44*log2(nodes+2)-0.3, MAXH is a safe upper bound. */
8 | #define MAXH (sizeof(void*)*8*3/2)
9 |
10 | struct node {
11 | const void *key;
12 | void *a[2];
13 | int h;
14 | };
15 |
16 | int __tsearch_balance(void **);
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/utils.c:
--------------------------------------------------------------------------------
1 | /* redsocks - transparent TCP-to-proxy redirector
2 | * Copyright (C) 2007-2018 Leonid Evdokimov
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 | * use this file except in compliance with the License. You may obtain a copy
6 | * of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include "log.h"
26 | #include "base.h"
27 | #include "utils.h"
28 | #include "redsocks.h" // for redsocks_close
29 | #include "libc-compat.h"
30 |
31 | int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr)
32 | {
33 | socklen_t addrlen = sizeof(*inaddr);
34 | ssize_t pktlen;
35 | struct msghdr msg;
36 | struct iovec io;
37 | char control[1024];
38 |
39 | memset(&msg, 0, sizeof(msg));
40 | msg.msg_name = inaddr;
41 | msg.msg_namelen = sizeof(*inaddr);
42 | msg.msg_iov = &io;
43 | msg.msg_iovlen = 1;
44 | msg.msg_control = control;
45 | msg.msg_controllen = sizeof(control);
46 | io.iov_base = buf;
47 | io.iov_len = buflen;
48 |
49 | pktlen = recvmsg(fd, &msg, 0);
50 | if (pktlen == -1) {
51 | log_errno(LOG_WARNING, "recvfrom");
52 | return -1;
53 | }
54 |
55 | if (toaddr) {
56 | memset(toaddr, 0, sizeof(*toaddr));
57 | for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
58 | if (
59 | cmsg->cmsg_level == SOL_IP &&
60 | cmsg->cmsg_type == IP_ORIGDSTADDR &&
61 | cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr))
62 | ) {
63 | struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg);
64 | char buf[RED_INET_ADDRSTRLEN];
65 | log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf)));
66 | memcpy(toaddr, cmsgaddr, sizeof(*toaddr));
67 | }
68 | else {
69 | log_error(LOG_WARNING, "unexepcted cmsg (level,type) = (%d,%d)",
70 | cmsg->cmsg_level, cmsg->cmsg_type);
71 | }
72 | }
73 | if (toaddr->sin_family != AF_INET) {
74 | log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found");
75 | return -1;
76 | }
77 | }
78 |
79 | if (addrlen != sizeof(*inaddr)) {
80 | log_error(LOG_WARNING, "unexpected address length %u instead of %zu", addrlen, sizeof(*inaddr));
81 | return -1;
82 | }
83 |
84 | if (pktlen >= buflen) {
85 | char buf[RED_INET_ADDRSTRLEN];
86 | log_error(LOG_WARNING, "wow! Truncated udp packet of size %zd from %s! impossible! dropping it...",
87 | pktlen, red_inet_ntop(inaddr, buf, sizeof(buf)));
88 | return -1;
89 | }
90 |
91 | return pktlen;
92 | }
93 |
94 | uint32_t red_randui32()
95 | {
96 | uint32_t ret;
97 | evutil_secure_rng_get_bytes(&ret, sizeof(ret));
98 | return ret;
99 | }
100 |
101 | time_t redsocks_time(time_t *t)
102 | {
103 | time_t retval;
104 | retval = time(t);
105 | if (retval == ((time_t) -1))
106 | log_errno(LOG_WARNING, "time");
107 | return retval;
108 | }
109 |
110 | int redsocks_gettimeofday(struct timeval *tv)
111 | {
112 | int retval = gettimeofday(tv, NULL);
113 | if (retval != 0)
114 | log_errno(LOG_WARNING, "gettimeofday");
115 | return retval;
116 | }
117 |
118 | char *redsocks_evbuffer_readline(struct evbuffer *buf)
119 | {
120 | #if _EVENT_NUMERIC_VERSION >= 0x02000000
121 | return evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF);
122 | #else
123 | return evbuffer_readline(buf);
124 | #endif
125 | }
126 |
127 | int red_socket_client(int type)
128 | {
129 | int fd = -1;
130 | int error;
131 |
132 | fd = socket(AF_INET, type, 0);
133 | if (fd == -1) {
134 | log_errno(LOG_ERR, "socket");
135 | goto fail;
136 | }
137 |
138 | error = fcntl_nonblock(fd);
139 | if (error) {
140 | log_errno(LOG_ERR, "fcntl");
141 | goto fail;
142 | }
143 |
144 | if (type == SOCK_STREAM) {
145 | if (apply_tcp_keepalive(fd))
146 | goto fail;
147 | }
148 |
149 | return fd;
150 |
151 | fail:
152 | if (fd != -1)
153 | redsocks_close(fd);
154 | return -1;
155 | }
156 |
157 | int red_socket_server(int type, struct sockaddr_in *bindaddr)
158 | {
159 | int on = 1;
160 | int error;
161 | int fd = red_socket_client(type);
162 | if (fd == -1)
163 | goto fail;
164 |
165 | error = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
166 | if (error) {
167 | log_errno(LOG_ERR, "setsockopt");
168 | goto fail;
169 | }
170 |
171 | error = bind(fd, (struct sockaddr*)bindaddr, sizeof(*bindaddr));
172 | if (error) {
173 | log_errno(LOG_ERR, "bind");
174 | goto fail;
175 | }
176 |
177 | return fd;
178 | fail:
179 | if (fd != -1)
180 | redsocks_close(fd);
181 | return -1;
182 |
183 | }
184 |
185 |
186 | struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg)
187 | {
188 | struct bufferevent *retval = NULL;
189 | int relay_fd = -1;
190 | int error;
191 |
192 | relay_fd = red_socket_client(SOCK_STREAM);
193 |
194 | error = connect(relay_fd, (struct sockaddr*)addr, sizeof(*addr));
195 | if (error && errno != EINPROGRESS) {
196 | log_errno(LOG_NOTICE, "connect");
197 | goto fail;
198 | }
199 |
200 | retval = bufferevent_new(relay_fd, NULL, writecb, errorcb, cbarg);
201 | if (!retval) {
202 | log_errno(LOG_ERR, "bufferevent_new");
203 | goto fail;
204 | }
205 |
206 | relay_fd = -1;
207 |
208 | error = bufferevent_enable(retval, EV_WRITE); // we wait for connection...
209 | if (error) {
210 | log_errno(LOG_ERR, "bufferevent_enable");
211 | goto fail;
212 | }
213 |
214 | return retval;
215 |
216 | fail:
217 | if (relay_fd != -1)
218 | redsocks_close(relay_fd);
219 | if (retval)
220 | redsocks_bufferevent_free(retval);
221 | return NULL;
222 | }
223 |
224 | int red_socket_geterrno(struct bufferevent *buffev)
225 | {
226 | int error;
227 | int pseudo_errno;
228 | socklen_t optlen = sizeof(pseudo_errno);
229 |
230 | assert(event_get_fd(&buffev->ev_read) == event_get_fd(&buffev->ev_write));
231 |
232 | error = getsockopt(event_get_fd(&buffev->ev_read), SOL_SOCKET, SO_ERROR, &pseudo_errno, &optlen);
233 | if (error) {
234 | log_errno(LOG_ERR, "getsockopt");
235 | return -1;
236 | }
237 | return pseudo_errno;
238 | }
239 |
240 | /** simple fcntl(2) wrapper, provides errno and all logging to caller
241 | * I have to use it in event-driven code because of accept(2) (see NOTES)
242 | * and connect(2) (see ERRORS about EINPROGRESS)
243 | */
244 | int fcntl_nonblock(int fd)
245 | {
246 | int error;
247 | int flags;
248 |
249 | flags = fcntl(fd, F_GETFL);
250 | if (flags == -1)
251 | return -1;
252 |
253 | error = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
254 | if (error)
255 | return -1;
256 |
257 | return 0;
258 | }
259 |
260 | int red_is_socket_connected_ok(struct bufferevent *buffev)
261 | {
262 | int pseudo_errno = red_socket_geterrno(buffev);
263 |
264 | if (pseudo_errno == -1) {
265 | return 0;
266 | }
267 | else if (pseudo_errno) {
268 | errno = pseudo_errno;
269 | log_errno(LOG_NOTICE, "connect");
270 | return 0;
271 | }
272 | else {
273 | return 1;
274 | }
275 | }
276 |
277 | char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size)
278 | {
279 | const char *retval = 0;
280 | size_t len = 0;
281 | uint16_t port;
282 | const char placeholder[] = "???:???";
283 |
284 | assert(buffer_size >= RED_INET_ADDRSTRLEN);
285 |
286 | memset(buffer, 0, buffer_size);
287 | if (sa->sin_family == AF_INET) {
288 | retval = inet_ntop(AF_INET, &sa->sin_addr, buffer, buffer_size);
289 | port = ((struct sockaddr_in*)sa)->sin_port;
290 | }
291 | else if (sa->sin_family == AF_INET6) {
292 | retval = inet_ntop(AF_INET6, &((const struct sockaddr_in6*)sa)->sin6_addr, buffer, buffer_size);
293 | port = ((struct sockaddr_in6*)sa)->sin6_port;
294 | }
295 | if (retval) {
296 | assert(retval == buffer);
297 | len = strlen(retval);
298 | snprintf(buffer + len, buffer_size - len, ":%d", ntohs(port));
299 | }
300 | else {
301 | strcpy(buffer, placeholder);
302 | }
303 | return buffer;
304 | }
305 |
306 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
307 |
--------------------------------------------------------------------------------
/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef UTILS_H_SAT_FEB__2_02_24_05_2008
2 | #define UTILS_H_SAT_FEB__2_02_24_05_2008
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | struct sockaddr_in;
9 |
10 | #define SIZEOF_ARRAY(arr) (sizeof(arr) / sizeof(arr[0]))
11 | #define FOREACH(ptr, array) for (ptr = array; ptr < array + SIZEOF_ARRAY(array); ptr++)
12 | #define FOREACH_REV(ptr, array) for (ptr = array + SIZEOF_ARRAY(array) - 1; ptr >= array; ptr--)
13 |
14 | #define UNUSED(x) ((void)(x))
15 |
16 | #if defined __GNUC__
17 | #define PACKED __attribute__((packed))
18 | #else
19 | #error Unknown compiler, modify utils.h for it
20 | #endif
21 |
22 |
23 | #ifdef __GNUC__
24 | #define member_type(type, member) __typeof(((type *)0)->member)
25 | #else
26 | #define member_type(type, member) const void
27 | #endif
28 |
29 | /**
30 | * container_of - cast a member of a structure out to the containing structure
31 | * @ptr: the pointer to the member.
32 | * @type: the type of the container struct this is embedded in.
33 | * @member: the name of the member within the struct.
34 | *
35 | */
36 | #define container_of(ptr, type, member) \
37 | ((type *)( \
38 | (char *)(member_type(type, member) *){ ptr } - offsetof(type, member) \
39 | ))
40 |
41 |
42 | #define clamp_value(value, min_val, max_val) do { \
43 | if (value < min_val) \
44 | value = min_val; \
45 | if (value > max_val) \
46 | value = max_val; \
47 | } while (0)
48 |
49 |
50 | uint32_t red_randui32();
51 | time_t redsocks_time(time_t *t);
52 | int redsocks_gettimeofday(struct timeval *tv);
53 | char *redsocks_evbuffer_readline(struct evbuffer *buf);
54 | struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg);
55 | int red_socket_geterrno(struct bufferevent *buffev);
56 | int red_socket_client(int type);
57 | int red_socket_server(int type, struct sockaddr_in *bindaddr);
58 | int red_is_socket_connected_ok(struct bufferevent *buffev);
59 | int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr);
60 |
61 | int fcntl_nonblock(int fd);
62 |
63 | #define event_fmt_str "%s|%s|%s|%s|%s|0x%x"
64 | #define event_fmt(what) \
65 | (what) & EVBUFFER_READ ? "EVBUFFER_READ" : "0", \
66 | (what) & EVBUFFER_WRITE ? "EVBUFFER_WRITE" : "0", \
67 | (what) & EVBUFFER_EOF ? "EVBUFFER_EOF" : "0", \
68 | (what) & EVBUFFER_ERROR ? "EVBUFFER_ERROR" : "0", \
69 | (what) & EVBUFFER_TIMEOUT ? "EVBUFFER_TIMEOUT" : "0", \
70 | (what) & ~(EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF|EVBUFFER_ERROR|EVBUFFER_TIMEOUT)
71 |
72 | #if INET6_ADDRSTRLEN < INET_ADDRSTRLEN
73 | # error Impossible happens: INET6_ADDRSTRLEN < INET_ADDRSTRLEN
74 | #else
75 | # define RED_INET_ADDRSTRLEN (INET6_ADDRSTRLEN + 1 + 5 + 1) // addr + : + port + \0
76 | #endif
77 | char *red_inet_ntop(const struct sockaddr_in* sa, char* buffer, size_t buffer_size);
78 |
79 | /* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
80 | /* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
81 | #endif /* UTILS_H_SAT_FEB__2_02_24_05_2008 */
82 |
--------------------------------------------------------------------------------
/version.h:
--------------------------------------------------------------------------------
1 | #ifndef VERSION_H_SUN_NOV_27_03_22_30_2011
2 | #define VERSION_H_SUN_NOV_27_03_22_30_2011
3 |
4 | extern const char* redsocks_version;
5 |
6 | #endif // VERSION_H_SUN_NOV_27_03_22_30_2011
7 |
--------------------------------------------------------------------------------