-hope springs eternal
[oweals/gnunet.git] / src / gns / proxy / gnunet-gns-proxy.py
1 #!/usr/bin/python
2
3 """
4 Copyright (c) 2001 SUZUKI Hisao 
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 
7
8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 
9
10 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 """
12
13 __doc__ = """Tiny HTTP Proxy.
14
15 This module implements GET, HEAD, POST, PUT and DELETE methods
16 on BaseHTTPServer, and behaves as an HTTP proxy.  The CONNECT
17 method is also implemented experimentally, but has not been
18 tested yet.
19
20 Any help will be greatly appreciated.   SUZUKI Hisao
21 """
22
23 __version__ = "0.2.1"
24
25 import BaseHTTPServer, select, socket, SocketServer, urlparse, re, string, os, sys
26
27 class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler):
28     __base = BaseHTTPServer.BaseHTTPRequestHandler
29     __base_handle = __base.handle
30
31     server_version = "TinyHTTPProxy/" + __version__
32     rbufsize = 0                        # self.rfile Be unbuffered
33     host_port = ()
34
35     def handle(self):
36         (ip, port) =  self.client_address
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         to_replace = ""
46         if i >= 0:
47           self.host_port = netloc[:i], int(netloc[i+1:])
48           if (re.match("(\w+\.)*gnunet$", self.host_port[0])):
49             print 'calling gnunet-gns -a '+netloc[:i]
50             auth = os.popen("gnunet-gns -a "+netloc[:i])
51             lines = auth.readlines()
52             if (len(lines) > 0):
53               print 'result: '+lines[0].split(" ")[-1].rstrip()
54               to_replace = lines[0].split(" ")[-1].rstrip()
55             else:
56               to_replace = "+"
57         else:
58           self.host_port = netloc, 80
59           if (re.match("(\w+\.)*gnunet$", self.host_port[0])):
60             print 'calling gnunet-gns -a '+netloc
61             auth = os.popen("gnunet-gns -a "+netloc)
62             lines = auth.readlines()
63             if (len(lines) > 0):
64               print 'result: '+lines[0].split(" ")[-1].rstrip()
65               to_replace = lines[0].split(" ")[-1].rstrip()
66             else:
67               to_replace = "+"
68
69         print "\t" "connect to %s:%d" % self.host_port
70         try: soc.connect(self.host_port)
71         except socket.error, arg:
72             try: msg = arg[1]
73             except: msg = arg
74             self.send_error(404, msg)
75             return (0, 0)
76         return (1, to_replace)
77
78     def do_CONNECT(self):
79         soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
80         try:
81             res, to_repl = self._connect_to(self.path, soc)
82             if res:
83                 self.log_request(200)
84                 self.wfile.write(self.protocol_version +
85                                  " 200 Connection established\r\n")
86                 self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
87                 self.wfile.write("\r\n")
88                 self._read_write(soc, to_repl, 300)
89         finally:
90             print "\t" "bye"
91             soc.close()
92             self.connection.close()
93     
94     def test_re2(self, mo):
95       short = os.popen("gnunet-gns -s"+string.replace(mo.group(1), 'a href="http://', ""))
96       lines = short.readlines()
97       if (len(lines) < 1):
98         return mo.group(1)
99       elif (len(lines[0].split(" ")) > 0):
100         return 'a href="http://'+lines[0].split(" ")[-1].rstrip()
101       else:
102         return mo.group(1)
103
104     def shorten_zkey(self):
105       return lambda mo: self.test_re2(mo)
106       #return lambda mo: 'a href="http://'+os.popen("gnunet-gns -s"+string.replace(mo.group(1), 'a href="http://', "")).readlines()[0].split(" ")[-1].rstrip()
107     
108     def test_re(self, to_repl, mo):
109       short = os.popen("gnunet-gns -s "+string.replace(mo.group(1)+to_repl, 'a href="http://', ""))
110       lines = short.readlines()
111       if (len(lines) < 1):
112         return to_repl
113       elif (len(lines[0].split(" ")) > 0):
114         return 'a href="http://'+lines[0].split(" ")[-1].rstrip()
115       else:
116         return to_repl
117
118     def replace_and_shorten(self, to_repl):
119       return lambda mo: self.test_re(to_repl, mo)
120     #  return lambda mo: 'a href="http://'+os.popen("gnunet-gns -s "+string.replace(mo.group(1)+to_repl, 'a href="http://', "")).readlines()[0].split(" ")[-1].rstrip()
121     #full = string.replace(mo.group(1)+to_repl, 'a href="http://', "")
122         #print 'calling gnunet-gns -s '+full
123         #s = os.popen("gnunet-gns -s "+full)
124         #lines = s.readlines()
125         #print 'short: '+lines[0].split(" ")[-1].rstrip()
126         #return 'a href="'+lines[0].split(" ")[-1].rstrip()
127
128     def do_GET(self):
129         (scm, netloc, path, params, query, fragment) = urlparse.urlparse(
130             self.path, 'http')
131         if scm != 'http' or fragment or not netloc:
132             self.send_error(400, "bad url %s" % self.path)
133             return
134         soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
135         try:
136             res, to_repl = self._connect_to(netloc, soc)
137             if res:
138                 self.log_request()
139                 soc.send("%s %s %s\r\n" % (
140                     self.command,
141                     urlparse.urlunparse(('', '', path, params, query, '')),
142                     self.request_version))
143                 if (re.match("(\w+\.)*gnunet$", self.headers['Host'])):
144                   leho = os.popen("gnunet-gns -t LEHO -u "+self.headers['Host']).readlines()
145                   if (len(leho) < 2):
146                     print "Legacy hostname lookup failed!"
147                   elif (len(leho) == 1):
148                     print "Legacy hostname not present!"
149                   else:
150                     newhost = leho[1].split(" ")[-1].rstrip()
151                     print "Changing Host: "+self.headers['Host']+" to "+newhost
152                     self.headers['Host'] = newhost
153                 self.headers['Connection'] = 'close'
154                 del self.headers['Proxy-Connection']
155                 del self.headers['Accept-Encoding']
156                 for key_val in self.headers.items():
157                     soc.send("%s: %s\r\n" % key_val)
158                 soc.send("\r\n")
159                 self._read_write(soc, to_repl)
160         finally:
161             print "\t" "bye"
162             soc.close()
163             self.connection.close()
164
165     def _read_write(self, soc, to_repl="", max_idling=20):
166         iw = [self.connection, soc]
167         ow = []
168         count = 0
169         msg = ''
170         while 1:
171             count += 1
172             (ins, _, exs) = select.select(iw, ow, iw, 3)
173             if exs:
174               break
175             if ins:
176                 for i in ins:
177                     if i is soc:
178                         out = self.connection
179                     else:
180                         out = soc
181                     data = i.recv(8192)
182                     if data:
183                         #try:
184                           data = re.sub(r'\nAccept-Ranges: \w+', r'', data)
185                           data = re.sub('(a href="http://(\w+\.)*zkey)',
186                               self.shorten_zkey(), data)
187                           if (re.match("(\w+\.)*gnunet$", self.host_port[0])):
188                               arr = self.host_port[0].split('.')
189                               arr.pop(0)
190                               data = re.sub('(a href="http://(\w+\.)*)(\+)',
191                                   self.replace_and_shorten(to_repl), data)
192                           out.send(data)
193                           count = 0
194                         #except:
195                         #  print "GNS exception:", sys.exc_info()[0]
196
197             else:
198                 print "\t" "idle", count
199                 print msg
200             if count == max_idling: break
201
202     do_HEAD = do_GET
203     do_POST = do_GET
204     do_PUT  = do_GET
205     do_DELETE=do_GET
206
207 class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
208                            BaseHTTPServer.HTTPServer): pass
209
210 if __name__ == '__main__':
211     from sys import argv
212     if argv[1:] and argv[1] in ('-h', '--help'):
213         print argv[0], "[port [allowed_client_name ...]]"
214     else:
215         if argv[2:]:
216             allowed = []
217             for name in argv[2:]:
218                 client = socket.gethostbyname(name)
219                 allowed.append(client)
220                 print "Accept: %s (%s)" % (client, name)
221             ProxyHandler.allowed_clients = allowed
222             del argv[2:]
223         else:
224             print "Any clients will be served..."
225         BaseHTTPServer.test(ProxyHandler, ThreadingHTTPServer)
226