]>
code.delx.au - monosys/blob - hacks/tcp-proxy
7 With mode=basic any incoming connections on listen_port will be proxied
8 to host:port. The proxy will only accept connections from hosts in the
11 With mode=proxy the first two lines of incoming connections must be
12 'host\nport\n'. Once again only connections from hosts in the [allowed]
13 section will be accepted. The proxy will connect to host:port and pass
14 bytes in both directions.
16 The final mode, mode=interceptor is designed to be combined with a firewall
17 rule and another instance running mode=proxy on another computer.
18 Proxies running in interceptor mode listen on all interfaces. They make
19 connections to the host:port specified in the config file, passing the
20 'capturedhost\ncapturedport\n' onto the destination. They then pass bytes
24 Example - Basic Forwarder
25 -------------------------
27 Config to forward all packets from port 8000 on localhost to google.com:80
28 Connections will be accepted from whatever IP address alpha.example.com
29 and beta.example.com point to.
38 host1 = alpha.example.com
39 host2 = beta.example.com
43 Example - Interceptor Proxy Combo
44 ---------------------------------
46 Capture all packets destined for port 1935 and send them to an interceptor
47 configured to listen on example.com:9997.
49 # iptables -t nat -A PREROUTING -p tcp --dport 1935
50 -j REDIRECT --to-ports 9997
51 # iptables -t nat -A OUTPUT -p tcp --dport 1935
52 -j REDIRECT --to-ports 9997
55 # ipfw add 50000 fwd 127.0.0.1,9997 tcp from any to any dst-port 1935
57 Config to forward these connections to proxy.example.com
62 host = proxy.example.com
67 Config file for proxy.example.com
74 host1 = alpha.example.com
75 host2 = beta.example.com
90 if sys
.platform
== "linux2":
92 socket
.SO_ORIGINAL_DST
93 except AttributeError:
94 # There is a missing const in the socket module... So we will add it now
95 socket
.SO_ORIGINAL_DST
= 80
97 def get_original_dest(sock
):
98 '''Gets the original destination address for connection that has been
99 redirected by netfilter.'''
100 # struct sockaddr_in {
101 # short sin_family; // e.g. AF_INET
102 # unsigned short sin_port; // e.g. htons(3490)
103 # struct in_addr sin_addr; // see struct in_addr, below
104 # char sin_zero[8]; // zero this if you want to
107 # unsigned long s_addr; // load with inet_aton()
109 # getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, (struct sockaddr_in *)&dstaddr, &dstlen);
111 data
= sock
.getsockopt(socket
.SOL_IP
, socket
.SO_ORIGINAL_DST
, 16)
112 _
, port
, a1
, a2
, a3
, a4
= struct
.unpack("!HHBBBBxxxxxxxx", data
)
113 address
= "%d.%d.%d.%d" % (a1
, a2
, a3
, a4
)
117 elif sys
.platform
== "darwin":
118 def get_original_dest(sock
):
119 '''Gets the original destination address for connection that has been
120 redirected by ipfw.'''
121 return sock
.getsockname()
125 class Proxy(asyncore
.dispatcher
):
126 def __init__(self
, arg
):
127 if isinstance(arg
, tuple):
128 asyncore
.dispatcher
.__init
__(self
)
129 self
.create_socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
132 asyncore
.dispatcher
.__init
__(self
, arg
)
140 def meet(self
, other
):
144 def handle_error(self
):
145 print("Proxy error:", traceback
.format_exc(), file=sys
.stderr
)
148 def handle_read(self
):
149 data
= self
.recv(8192)
151 self
.other
.buffer += data
153 def handle_write(self
):
154 sent
= self
.send(self
.buffer)
155 self
.buffer = self
.buffer[sent
:]
156 if len(self
.buffer) == 0 and self
.end
:
160 return len(self
.buffer) > 0
162 def handle_close(self
):
165 print("Proxy closed", file=sys
.stderr
)
167 if len(self
.other
.buffer) == 0:
169 self
.other
.end
= True
172 class ConnectProxy(asyncore
.dispatcher
):
173 def __init__(self
, sock
):
174 asyncore
.dispatcher
.__init
__(self
, sock
)
177 def handle_error(self
):
178 print("ConnectProxy error:", traceback
.format_exc(), file=sys
.stderr
)
181 def handle_read(self
):
182 self
.buffer += self
.recv(8192)
183 pos1
= self
.buffer.find("\n")
186 host
= self
.buffer[:pos1
].strip()
188 pos2
= self
.buffer[pos1
:].find("\n")
192 port
= int(self
.buffer[pos1
:pos2
].strip())
194 self
.buffer = self
.buffer[pos2
+1:]
195 self
.done(host
, port
)
197 def handle_write(self
):
200 def handle_close(self
):
201 print("Proxy closed", file=sys
.stderr
)
204 def done(self
, host
, port
):
205 print("Forwarding connection", host
, port
, file=sys
.stderr
)
207 # Create server proxy
208 server
= Proxy((host
, port
))
209 server
.buffer = self
.buffer
212 self
.__class
__ = Proxy
217 class BasicForwarder(asyncore
.dispatcher
):
218 def __init__(self
, listen_port
, host
, port
, allowed
):
219 asyncore
.dispatcher
.__init
__(self
)
222 self
.allowed
= allowed
223 self
.create_socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
224 self
.set_reuse_addr()
225 self
.bind((b
"", listen_port
))
227 print("BasicForwarder bound on", listen_port
, file=sys
.stderr
)
229 def handle_error(self
):
230 print("BasicForwarder error:", traceback
.format_exc(), file=sys
.stderr
)
232 def handle_accept(self
):
233 client_connection
, source_addr
= self
.accept()
234 if not self
.is_connected_allowed(source_addr
):
235 print("Rejected connection from", source_addr
, file=sys
.stderr
)
236 client_connection
.close()
239 print("Accepted connection from", source_addr
, file=sys
.stderr
)
241 # Hook the sockets up to the event loop
242 client
= Proxy(client_connection
)
243 server
= Proxy((self
.host
, self
.port
))
246 def is_connected_allowed(self
, source_addr
):
247 if len(self
.allowed
) == 1 and self
.allowed
[0].lower() == "all":
250 if source_addr
[0] in list(map(socket
.gethostbyname
, self
.allowed
)):
255 class Forwarder(asyncore
.dispatcher
):
256 def __init__(self
, listen_port
, allowed
):
257 asyncore
.dispatcher
.__init
__(self
)
258 self
.allowed
= allowed
259 self
.create_socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
260 self
.set_reuse_addr()
261 self
.bind((b
"", listen_port
))
263 print("Forwarder bound on", listen_port
, file=sys
.stderr
)
265 def handle_error(self
):
266 print("Forwarder error:", traceback
.format_exc(), file=sys
.stderr
)
268 def handle_accept(self
):
269 client_connection
, source_addr
= self
.accept()
270 if source_addr
[0] not in list(map(socket
.gethostbyname
, self
.allowed
)):
271 print("Rejected connection from", source_addr
, file=sys
.stderr
)
272 client_connection
.close()
275 print("Accepted connection from", source_addr
, file=sys
.stderr
)
276 ConnectProxy(client_connection
)
278 class Interceptor(asyncore
.dispatcher
):
279 def __init__(self
, listen_port
, host
, port
):
280 asyncore
.dispatcher
.__init
__(self
)
283 self
.create_socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
284 self
.set_reuse_addr()
285 self
.bind(("0.0.0.0", listen_port
))
287 print("Interceptor bound on", listen_port
, file=sys
.stderr
)
289 def handle_error(self
):
290 print("Interceptor error!", traceback
.format_exc(), file=sys
.stderr
)
292 def handle_accept(self
):
294 client_connection
, source_addr
= self
.accept()
295 dest
= get_original_dest(client_connection
)
296 print("Accepted connection from", source_addr
, file=sys
.stderr
)
298 # Hook them up to the event loop
299 client
= Proxy(client_connection
)
300 server
= Proxy((self
.host
, self
.port
))
301 server
.buffer += "%s\n%d\n" % dest
305 def main(listen_port
, host
, port
, mode
, allowed
):
307 proxy
= BasicForwarder(listen_port
, host
, port
, allowed
)
308 elif mode
== "proxy":
309 proxy
= Forwarder(listen_port
, allowed
)
310 elif mode
== "interceptor":
311 proxy
= Interceptor(listen_port
, host
, port
)
313 print("Unknown mode:", mode
, file=sys
.stderr
)
318 if __name__
== "__main__":
320 if sys
.argv
[1] == "-d":
326 except (IndexError, ValueError):
327 print("Usage: %s [-d] config" % sys
.argv
[0], file=sys
.stderr
)
331 c
= configparser
.RawConfigParser()
334 print("Error parsing config!", file=sys
.stderr
)
337 def guard(func
, message
):
341 print("Error:", message
, file=sys
.stderr
)
345 mode
= guard(lambda:c
.get("proxy", "mode").lower(),
346 "mode is a required field")
348 listen_port
= guard(lambda:c
.getint("proxy", "listen_port"),
349 "listen_port is a required field")
351 if mode
in ["basic", "interceptor"]:
352 text
= "%%s is a required field for mode=%s" % mode
353 host
= guard(lambda:c
.get("proxy", "host"), text
% "host")
354 port
= guard(lambda:c
.getint("proxy", "port"), text
% "port")
359 if mode
in ["basic", "proxy"]:
360 allowed
= guard(lambda:c
.items("allowed"),
361 "[allowed] section is required for mode=%s" % mode
)
362 allowed
= [h
for _
,h
in c
.items("allowed")]
369 main(listen_port
, host
, port
, mode
, allowed
)
370 except KeyboardInterrupt:
376 os
.open("/dev/null", os
.O_RDONLY
)
377 os
.open("/dev/null", os
.O_RDWR
)
383 sys
.exit(main(listen_port
, host
, port
, mode
, allowed
))
384 except KeyboardInterrupt: