Mercurial > hg > anonet-resdb
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 ()) |