]>
code.delx.au - monosys/blob - scripts/proxy.py
586459104405e4f9febc0a7b9caf44f016a530c5
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
89 if sys
.platform
== "linux2":
91 socket
.SO_ORIGINAL_DST
92 except AttributeError:
93 # There is a missing const in the socket module... So we will add it now
94 socket
.SO_ORIGINAL_DST
= 80
96 def get_original_dest(sock
):
97 '''Gets the original destination address for connection that has been
98 redirected by netfilter.'''
99 # struct sockaddr_in {
100 # short sin_family; // e.g. AF_INET
101 # unsigned short sin_port; // e.g. htons(3490)
102 # struct in_addr sin_addr; // see struct in_addr, below
103 # char sin_zero[8]; // zero this if you want to
106 # unsigned long s_addr; // load with inet_aton()
108 # getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, (struct sockaddr_in *)&dstaddr, &dstlen);
110 data
= sock
.getsockopt(socket
.SOL_IP
, socket
.SO_ORIGINAL_DST
, 16)
111 _
, port
, a1
, a2
, a3
, a4
= struct
.unpack("!HHBBBBxxxxxxxx", data
)
112 address
= "%d.%d.%d.%d" % (a1
, a2
, a3
, a4
)
116 elif sys
.platform
== "darwin":
117 def get_original_dest(sock
):
118 '''Gets the original destination address for connection that has been
119 redirected by ipfw.'''
120 return sock
.getsockname()
124 class Proxy(asyncore
.dispatcher
):
125 def __init__(self
, sock
):
126 asyncore
.dispatcher
.__init
__(self
, sock
)
130 def meet(self
, other
):
134 def handle_error(self
):
135 print >>sys
.stderr
, "Proxy error:", sys
.exc_info()
138 def handle_read(self
):
139 data
= self
.recv(8192)
141 self
.other
.buffer += data
143 def handle_write(self
):
144 sent
= self
.send(self
.buffer)
145 self
.buffer = self
.buffer[sent
:]
148 return len(self
.buffer) > 0
150 def handle_close(self
):
153 print >>sys
.stderr
, "Proxy closed"
158 class ConnectProxy(asyncore
.dispatcher
):
159 def __init__(self
, sock
):
160 asyncore
.dispatcher
.__init
__(self
, sock
)
163 def handle_error(self
):
164 print >>sys
.stderr
, "ConnectProxy error:", sys
.exc_info()
167 def handle_read(self
):
168 self
.buffer += self
.recv(8192)
169 pos1
= self
.buffer.find("\n")
172 host
= self
.buffer[:pos1
].strip()
174 pos2
= self
.buffer[pos1
:].find("\n")
178 port
= int(self
.buffer[pos1
:pos2
].strip())
180 self
.buffer = self
.buffer[pos2
+1:]
181 self
.done(host
, port
)
183 def handle_write(self
):
186 def handle_close(self
):
187 print >>sys
.stderr
, "Proxy closed"
190 def done(self
, host
, port
):
191 print >>sys
.stderr
, "Forwarding connection", host
, port
193 # Create server proxy
194 server_connection
= socket
.socket()
195 server_connection
.connect((host
, port
))
196 server
= Proxy(server_connection
)
199 self
.__class
__ = Proxy
201 server
.buffer = self
.buffer
205 class BasicForwarder(asyncore
.dispatcher
):
206 def __init__(self
, listen_port
, host
, port
, allowed
):
207 asyncore
.dispatcher
.__init
__(self
)
210 self
.allowed
= allowed
211 self
.create_socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
212 self
.bind(("", listen_port
))
215 def handle_error(self
):
216 print >>sys
.stderr
, "BasicForwarder error:", sys
.exc_info()
218 def handle_accept(self
):
219 client_connection
, (addr
, port
) = self
.accept()
220 if addr
not in map(socket
.gethostbyname
, self
.allowed
):
221 print >>sys
.stderr
, "Rejected connection from", addr
222 client_connection
.close()
225 print >>sys
.stderr
, "Accepted connection from", addr
, port
226 server_connection
= socket
.socket()
227 server_connection
.connect((self
.host
, self
.port
))
229 # Hook the sockets up to the event loop
230 client
= Proxy(client_connection
)
231 server
= Proxy(server_connection
)
234 class Forwarder(asyncore
.dispatcher
):
235 def __init__(self
, listen_port
, allowed
):
236 asyncore
.dispatcher
.__init
__(self
)
237 self
.allowed
= allowed
238 self
.create_socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
239 self
.bind(("", listen_port
))
242 def handle_error(self
):
243 print >>sys
.stderr
, "Forwarder error:", sys
.exc_info()
245 def handle_accept(self
):
246 client_connection
, (addr
, port
) = self
.accept()
247 if addr
not in map(socket
.gethostbyname
, self
.allowed
):
248 print >>sys
.stderr
, "Rejected connection from", addr
249 client_connection
.close()
252 print >>sys
.stderr
, "Accepted connection from", addr
, port
253 ConnectProxy(client_connection
)
255 class Interceptor(asyncore
.dispatcher
):
256 def __init__(self
, listen_port
, host
, port
):
257 asyncore
.dispatcher
.__init
__(self
)
260 self
.create_socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
261 self
.bind(("0.0.0.0", listen_port
))
264 def handle_error(self
):
265 print >>sys
.stderr
, "Interceptor error!", sys
.exc_info()
267 def handle_accept(self
):
269 client_connection
, addr
= self
.accept()
270 dest
= get_original_dest(client_connection
)
271 print >>sys
.stderr
, "Accepted connection from", addr
, port
272 server_connection
= socket
.socket()
273 server_connection
.connect((self
.host
, self
.port
))
275 # Hook them up to the event loop
276 client
= Proxy(client_connection
)
277 server
= Proxy(server_connection
)
278 server
.buffer += "%s\n%d\n" % dest
282 def main(listen_port
, host
, port
, mode
, allowed
):
284 proxy
= BasicForwarder(listen_port
, host
, port
, allowed
)
285 elif mode
== "proxy":
286 proxy
= Forwarder(listen_port
, allowed
)
287 elif mode
== "interceptor":
288 proxy
= Interceptor(listen_port
, host
, port
)
290 print >>sys
.stderr
, "Unknown mode:", mode
295 if __name__
== "__main__":
297 if sys
.argv
[1] == "-d":
303 except (IndexError, ValueError):
304 print >>sys
.stderr
, "Usage: %s [-d] config" % sys
.argv
[0]
308 c
= ConfigParser
.RawConfigParser()
311 print >>sys
.stderr
, "Error parsing config!"
314 def guard(func
, message
):
318 print >>sys
.stderr
, "Error:", message
322 mode
= guard(lambda:c
.get("proxy", "mode").lower(),
323 "mode is a required field")
325 listen_port
= guard(lambda:c
.getint("proxy", "listen_port"),
326 "listen_port is a required field")
328 if mode
in ["basic", "interceptor"]:
329 text
= "%%s is a required field for mode=%s" % mode
330 host
= guard(lambda:c
.get("proxy", "host"), text
% "host")
331 port
= guard(lambda:c
.getint("proxy", "port"), text
% "port")
336 if mode
in ["basic", "proxy"]:
337 allowed
= guard(lambda:c
.items("allowed"),
338 "[allowed] section is required for mode=%s" % mode
)
339 allowed
= [h
for _
,h
in c
.items("allowed")]
346 main(listen_port
, host
, port
, mode
, allowed
)
347 except KeyboardInterrupt:
353 os
.open("/dev/null", os
.O_RDONLY
)
354 os
.open("/dev/null", os
.O_RDWR
)
360 sys
.exit(main(listen_port
, host
, port
, allowed
))
361 except KeyboardInterrupt: