comparison contrib/anoproxy/anoproxy @ 672:c514819cc79c draft

Added AnoProxy, simple tool to create an outbound proxy from AnoNet(2)
author d3v11 <d3v11@d3v11.ano>
date Sat, 15 Oct 2011 19:45:58 -0500
parents
children
comparison
equal deleted inserted replaced
671:1bc0c37ce2c1 672:c514819cc79c
1 #!/usr/bin/python
2
3 __doc__ = """AnoProxy
4
5 AnoProxy is a modified version of TinyHTTPProxy. Many thanks
6 to Suzuki Hisao and Mitko Haralanov for the original source.
7
8 AnoProxy provides: GET, HEAD, POST, PUT , DELETE, and CONNECT
9 """
10
11 __version__ = "x.x.x"
12
13 import BaseHTTPServer, select, socket, SocketServer, urlparse
14 import logging
15 import logging.handlers
16 import getopt
17 import sys
18 import os
19 import signal
20 import threading
21 from types import FrameType, CodeType
22 from time import sleep
23 import ftplib
24
25 DEFAULT_LOG_FILENAME = "proxy.log"
26
27 class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler):
28 __base = BaseHTTPServer.BaseHTTPRequestHandler
29 __base_handle = __base.handle
30
31 server_version = "AnoProxy/" + __version__
32 rbufsize = 0 # self.rfile Be unbuffered
33
34 def handle(self):
35 (ip, port) = self.client_address
36 self.server.logger.log (logging.INFO, "Request from '%s'", ip)
37 if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
38 self.raw_requestline = self.rfile.readline()
39 if self.parse_request(): self.send_error(403)
40 else:
41 self.__base_handle()
42
43 def _connect_to(self, netloc, soc):
44 i = netloc.find(':')
45 if i >= 0:
46 host_port = netloc[:i], int(netloc[i+1:])
47 else:
48 host_port = netloc, 80
49 self.server.logger.log (logging.INFO, "connect to %s:%d", host_port[0], host_port[1])
50 try: soc.connect(host_port)
51 except socket.error, arg:
52 try: msg = arg[1]
53 except: msg = arg
54 self.send_error(404, msg)
55 return 0
56 return 1
57
58 def do_CONNECT(self):
59 soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
60 try:
61 if self._connect_to(self.path, soc):
62 self.log_request(200)
63 self.wfile.write(self.protocol_version +
64 " 200 Connection established\r\n")
65 self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
66 self.wfile.write("\r\n")
67 self._read_write(soc, 300)
68 finally:
69 soc.close()
70 self.connection.close()
71
72 def do_GET(self):
73 (scm, netloc, path, params, query, fragment) = urlparse.urlparse(
74 self.path, 'http')
75 if scm not in ('http', 'ftp') or fragment or not netloc:
76 self.send_error(400, "bad url %s" % self.path)
77 return
78 soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
79 try:
80 if scm == 'http':
81 if self._connect_to(netloc, soc):
82 self.log_request()
83 soc.send("%s %s %s\r\n" % (self.command,
84 urlparse.urlunparse(('', '', path,
85 params, query,
86 '')),
87 self.request_version))
88 self.headers['Connection'] = 'close'
89 del self.headers['Proxy-Connection']
90 for key_val in self.headers.items():
91 soc.send("%s: %s\r\n" % key_val)
92 soc.send("\r\n")
93 self._read_write(soc)
94 elif scm == 'ftp':
95 # fish out user and password information
96 i = netloc.find ('@')
97 if i >= 0:
98 login_info, netloc = netloc[:i], netloc[i+1:]
99 try: user, passwd = login_info.split (':', 1)
100 except ValueError: user, passwd = "anonymous", None
101 else: user, passwd ="anonymous", None
102 self.log_request ()
103 try:
104 ftp = ftplib.FTP (netloc)
105 ftp.login (user, passwd)
106 if self.command == "GET":
107 ftp.retrbinary ("RETR %s"%path, self.connection.send)
108 ftp.quit ()
109 except Exception, e:
110 self.server.logger.log (logging.WARNING, "FTP Exception: %s",
111 e)
112 finally:
113 soc.close()
114 self.connection.close()
115
116 def _read_write(self, soc, max_idling=20, local=False):
117 iw = [self.connection, soc]
118 local_data = ""
119 ow = []
120 count = 0
121 while 1:
122 count += 1
123 (ins, _, exs) = select.select(iw, ow, iw, 1)
124 if exs: break
125 if ins:
126 for i in ins:
127 if i is soc: out = self.connection
128 else: out = soc
129 data = i.recv(8192)
130 if data:
131 if local: local_data += data
132 else: out.send(data)
133 count = 0
134 if count == max_idling: break
135 if local: return local_data
136 return None
137
138 do_HEAD = do_GET
139 do_POST = do_GET
140 do_PUT = do_GET
141 do_DELETE=do_GET
142
143 def log_message (self, format, *args):
144 self.server.logger.log (logging.INFO, "%s %s", self.address_string (),
145 format % args)
146
147 def log_error (self, format, *args):
148 self.server.logger.log (logging.ERROR, "%s %s", self.address_string (),
149 format % args)
150
151 class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
152 BaseHTTPServer.HTTPServer):
153 def __init__ (self, server_address, RequestHandlerClass, logger=None):
154 BaseHTTPServer.HTTPServer.__init__ (self, server_address,
155 RequestHandlerClass)
156 self.logger = logger
157
158 def logSetup (filename, log_size, daemon):
159 logger = logging.getLogger ("AnoProxy")
160 logger.setLevel (logging.INFO)
161 if not filename:
162 if not daemon:
163 # display to the screen
164 handler = logging.StreamHandler ()
165 else:
166 handler = logging.handlers.RotatingFileHandler (DEFAULT_LOG_FILENAME,
167 maxBytes=(log_size*(1<<20)),
168 backupCount=5)
169 else:
170 handler = logging.handlers.RotatingFileHandler (filename,
171 maxBytes=(log_size*(1<<20)),
172 backupCount=5)
173 fmt = logging.Formatter ("[%(asctime)-12s.%(msecs)03d] "
174 "%(levelname)-8s {%(name)s %(threadName)s}"
175 " %(message)s",
176 "%Y-%m-%d %H:%M:%S")
177 handler.setFormatter (fmt)
178
179 logger.addHandler (handler)
180 return logger
181
182 def usage (msg=None):
183 if msg: print msg
184 print sys.argv[0], "[-s server] [-p port] [-l logfile] [-dh] [allowed_client_name ...]]"
185 print
186 print " -s - server address to bind to"
187 print " -p - Port to bind to"
188 print " -l - Path to logfile. If not specified, STDOUT is used"
189 print " -d - Run in the background"
190 print
191
192 def handler (signo, frame):
193 while frame and isinstance (frame, FrameType):
194 if frame.f_code and isinstance (frame.f_code, CodeType):
195 if "run_event" in frame.f_code.co_varnames:
196 frame.f_locals["run_event"].set ()
197 return
198 frame = frame.f_back
199
200 def daemonize (logger):
201 class DevNull (object):
202 def __init__ (self): self.fd = os.open ("/dev/null", os.O_WRONLY)
203 def write (self, *args, **kwargs): return 0
204 def read (self, *args, **kwargs): return 0
205 def fileno (self): return self.fd
206 def close (self): os.close (self.fd)
207 class ErrorLog:
208 def __init__ (self, obj): self.obj = obj
209 def write (self, string): self.obj.log (logging.ERROR, string)
210 def read (self, *args, **kwargs): return 0
211 def close (self): pass
212
213 if os.fork () != 0:
214 ## allow the child pid to instanciate the server
215 ## class
216 sleep (1)
217 sys.exit (0)
218 os.setsid ()
219 fd = os.open ('/dev/null', os.O_RDONLY)
220 if fd != 0:
221 os.dup2 (fd, 0)
222 os.close (fd)
223 null = DevNull ()
224 log = ErrorLog (logger)
225 sys.stdout = null
226 sys.stderr = log
227 sys.stdin = null
228 fd = os.open ('/dev/null', os.O_WRONLY)
229 #if fd != 1: os.dup2 (fd, 1)
230 os.dup2 (sys.stdout.fileno (), 1)
231 if fd != 2: os.dup2 (fd, 2)
232 if fd not in (1, 2): os.close (fd)
233
234 def main ():
235 logfile = None
236 daemon = False
237 max_log_size = 20
238 port = 8000
239 allowed = []
240 run_event = threading.Event ()
241 local_hostname = socket.gethostname ()
242
243 try: opts, args = getopt.getopt (sys.argv[1:], "l:dhp:s:", [])
244 except getopt.GetoptError, e:
245 usage (str (e))
246 return 1
247
248 for opt, value in opts:
249 if opt == "-s": SERVER = value
250 if opt == "-p": port = int (value)
251 if opt == "-l": logfile = value
252 if opt == "-d": daemon = not daemon
253 if opt == "-h":
254 usage ()
255 return 0
256
257 # setup the log file
258 logger = logSetup (logfile, max_log_size, daemon)
259
260 if daemon:
261 daemonize (logger)
262 signal.signal (signal.SIGINT, handler)
263
264 if args:
265 allowed = []
266 for name in args:
267 client = socket.gethostbyname(name)
268 allowed.append(client)
269 logger.log (logging.INFO, "Accept: %s (%s)" % (client, name))
270 ProxyHandler.allowed_clients = allowed
271 else:
272 logger.log (logging.INFO, "Any clients will be served...")
273
274 server_address = (SERVER, port)
275 ProxyHandler.protocol = "HTTP/1.0"
276 httpd = ThreadingHTTPServer (server_address, ProxyHandler, logger)
277 sa = httpd.socket.getsockname ()
278 print "Servering HTTP on", sa[0], "port", sa[1]
279 req_count = 0
280 while not run_event.isSet ():
281 try:
282 httpd.handle_request ()
283 req_count += 1
284 if req_count == 1000:
285 logger.log (logging.INFO, "Number of active threads: %s",
286 threading.activeCount ())
287 req_count = 0
288 except select.error, e:
289 if e[0] == 4 and run_event.isSet (): pass
290 else:
291 logger.log (logging.CRITICAL, "Errno: %d - %s", e[0], e[1])
292 logger.log (logging.INFO, "Server shutdown")
293 return 0
294
295 if __name__ == '__main__':
296 sys.exit (main ())