#!/usr/bin/python # ---------------------------------------------------------------------- # gtoproxy4 - Postbank Girotel Online proxy, vierde versie # # 2003-08-05, Dick Streefland # ---------------------------------------------------------------------- import sys, getopt, string, re, socket, thread, select loc_host = "INADDR_ANY" loc_port = 2000 rem_host = 'gto.postbank.nl' rem_port = 'https' logfile = None opt = {} usage = """Usage: gtoproxy4 [options] [ []] Options: -h specify local host name for bind() (default: %(loc_host)s) -p specify local port for bind() (default: %(loc_port)d) -l enable logging to file (specify - for stdout) -s use a SSL connection to the remote host (default for https) -1 process only a single connection at a time -q stop after processing a single connection -v be verbose""" % vars() optdef = "h:p:l:s1qv" escapes = { '\0': r'\0', '\t': r'\t', '\n': r'\n', '\r': r'\r', '\\': r'\\' } patches = { # Stel initialisatie uit tot na het klikken op "Inloggen": r'onLoad="javascript:top...\(\)"': '', # Verander even de achtergrond kleur: groen = OK, rood = not OK 'class="gtobody"': 'class="gtobody" id="gtobodyid"', r'(..\(\){..\(\);)': r'\1' 'document.getElementById("gtobodyid").style["background"] = ' 'top.frames.Onder.frames.Mosaic.document.g1.Applet_is_loaded ' '? "#00ff00" : "#ff0000";', # cookies met "secure" flag werken niet met http: ' secure=true;': '', # cookies worden niet opgeslagen met "domain=.postbank.nl": r'"; domain=" \+ .. \+': '', # voeg namen toe aan de gironummers (voorbeeld): #'value="4444444">4444444': 'value="4444444">4444444 (Prive)', #'value="5555555">5555555': 'value="5555555">5555555 (Zakelijk)', } def error(fmt, *arg): "Print error message and abort." print fmt % arg sys.exit(1) def verbose(fmt, *arg): "Optionally show verbose message and add it to the log file." if opt['v']: print fmt % arg log(fmt + '\n', *arg) def log(fmt, *arg): "Add a string to the log file, if any." if logfile: loglock.acquire() logfile.write(fmt % arg) logfile.flush() loglock.release() def portnum(service): "Convert a TCP service name or ASCII port number to an integer." try: if service[0] in string.digits: p = string.atoi(service) else: p = socket.getservbyname(service, 'tcp') except: error('Unknown service "%s"', service) return p def esc(data): "Escape special characters." str = "" for b in data: if escapes.has_key(b): b = escapes[b] elif b < ' ' or b > '~': b = r'\x%02x' % ord(b) str = str + b return str def listen(host, port): "Create a socket for listening." try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((host, port)) s.listen(5) except Exception, e: error(e[1]) return s class Connection: "A socket-like object representing either a plain or SSL connection." def __init__(self, host, port, ssl = 0): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((host, port)) self.fileno = self.socket.fileno self.shutdown = self.socket.shutdown if ssl: self.ssl = socket.ssl(self.socket, None, None) self.recv = self.ssl.read self.send = self.ssl.write else: self.ssl = None self.recv = self.socket.recv self.send = self.socket.send def __del__(self): self.socket.close() def copy(label, src, dst): "Copy a chunk of data; return 0 on EOF." try: buf = src.recv(4096) except: buf = "" if not buf: log('%s shutdown\n', label) try: dst.shutdown(1) except: pass return 0 # ---------------------------------------------------------------------- # Modificaties voor Girotel Online: if label[-3:] == '==>': # Gebruik hostnaam uit de Host: header, als die beschikbaar is: if dst.host == None: m = re.search(r'Host: (\S+)', buf) if m: dst.host = m.groups()[0] else: dst.host = 'localhost:%d' % loc_port buf = buf.replace('Host: ' + dst.host, 'Host: ' + rem_host) buf = buf.replace('http://' + dst.host, 'https://' + rem_host) elif src.host: buf = buf.replace('https://' + rem_host, 'http://' + src.host) for key in patches.keys(): buf = re.sub(key, patches[key], buf) # ---------------------------------------------------------------------- if logfile: s = esc(buf) c = '"\n%s "' % label[:-3] s = string.replace(s[:-1], r'\n', r'\n' + c) + s[-1] log('%s "%s"\n', label, s) try: dst.send(buf) except Exception, e: log('%s send() failure: %s\n', label, e[1]) return 1 def proxy(tid, local, addr, rem_host, rem_port): "Handle a single proxy connection." prefix = "%03d " % tid loc = "%s:%d" % addr rem = "%s:%d" % (rem_host, rem_port) verbose("%sopen %s --> %s", prefix, loc, rem) try: remote = Connection(rem_host, rem_port, opt['s']) remote.host = None # "don't try this at home" except Exception, e: verbose('%serror: %s', prefix, e[1]) local.close() return fdset = [local, remote] while fdset: input = select.select(fdset, [], [])[0] if local in input: if not copy(prefix + '==>', local, remote): fdset.remove(local) if remote in input: if not copy(prefix + '<==', remote, local): fdset.remove(remote) local.close() verbose("%sclose", prefix) def proxy_thread(tid, local, addr, rem_host, rem_port): "Thread to handle a single proxy connection." proxy(tid, local, addr, rem_host, rem_port) thread.exit() def cmdline(argv): "Parse the command line." global loc_host, loc_port, rem_host, rem_port, logfile, opt try: options, arguments = getopt.getopt(argv, optdef) if len(arguments) > 0: rem_host = arguments.pop(0) if len(arguments) > 0: rem_port = arguments.pop(0) except: error(usage) for c in optdef: opt[c] = 0 for (option, arg) in options: opt[option[1]] = 1 if option == "-h": loc_host = arg if option == "-l": if arg == "-": logfile = sys.stdout else: logfile = open(arg, "w") if option == "-p": loc_port = portnum(arg) rem_port = portnum(rem_port) if rem_port == 443: opt['s'] = 1 def main(): global loglock cmdline(sys.argv[1:]) loglock = thread.allocate_lock() host = loc_host if host == 'INADDR_ANY': host = '' sock = listen(host, loc_port) verbose("Listening on %s:%d", loc_host, loc_port) tid = 0 while 1: (local, addr) = sock.accept() tid += 1 if opt['1'] or opt['q']: proxy(tid, local, addr, rem_host, rem_port) if opt['q']: break else: thread.start_new(proxy_thread, (tid, local, addr, rem_host, rem_port)) try: main() except KeyboardInterrupt: pass