WebProxy46
Aus Si:Wiki von Siegrist SystemLösungen - Informatik und Rezepte
Version vom 9. September 2015, 23:17 Uhr von Sigi (Diskussion | Beiträge)
- Dieses Programm nimmt HTTP, HTTPS und FTP Anfragen von IPv4 Clients entgegen und leitet diese, wenn möglich, an IPv6 Server weiter.
- Es löst die Namen der GET Anfragen selbst auf um, wenn möglich, eine IPv6 Adresse zu erhalten. Ist keine solche vorhanden, nehmen wir halt IPv4 um möglichst alle Requests einer Seite zu befriedigen.
- Alle gängigen Methoden wie GET, HEAD, POST, PUT, OPTIONS, TRACE und DELETE sowie CONNECT für SSL Verbindungen werden unterstützt.
#!/usr/bin/python # Copyright (c) 2013 by Peter_Siegrist(SystemLoesungen) (PSS @ ZweierNet.ch) # www.IPv6Tech.ch # # All Rights reserved. # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # # This is a simple IPv4-IPv6 non-caching HTTP/HTTPS Proxy/Gateway. # The Server can make both IPv6, primarily, or IPv4 requestst on behalf of the pure IPv4 clients. # # WebProxy46 does not filter any messages apart from chanching HTTP/1.1 to HTTP/1.0 # because we do not want to filter out subrequests in persistent connections. # # All standard methodes are supported: GET, HEAD, POST, PUT, OPTIONS, TRACE and DELETE. # The CONNECT method is used for SSL connections. Only port 443 is allowed with this method (HTTP CONNECT Vulnerability) # # For using this proxy configure your browser to use proxy service. # The servers standard binding is port 9090 on address 0.0.0.0 # # Accepted schemes are HTTP HTTPS and FTP. # Hostnames in URL's may be given as FQDN, IPv4 dotted address or IPv6 address in square brackets form [xx:xx:xx::x] # import socket import getopt import sys from select import select import logging import thread import struct import string #-- Vars version = 'WebProxy46/v0.7' buflen = 8192 listen_host = '0.0.0.0' listen_port = 9090 debug = False lfnr = 0 #-- Threaded Main Object class ProxyHandler: def __init__(self, local_conn, local_address): global lfnr lfnr += 1 self.lfnr = lfnr log(self.lfnr,'NEW Connect ...') self.local_client = local_conn self.local_client_address = local_address self.local_client_buffer = '' self.timeout = 60 self.handle() self.local_client.close() def _set_target_sockopts_ssl(self): self.target.setsockopt(socket.SOL_SOCKET,socket.MSG_DONTWAIT,1) self.target.setblocking(1) def handle(self): try: self.local_client.setblocking(1) self.local_client.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 20)) #self.local_client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0) clt_count = 0 while 1: clt_count += 1 (client_data, _, client_error) = select([self.local_client], [], [], 1) if client_data: data = self.local_client.recv(buflen) if len(data) == 0: break self.local_client_buffer += data log2(self.lfnr,"data:%s"%data) if client_error: err = select.error log(self.lfnr,"%s - Client_Error select: %s on %s " % (self.af, err, self.local_client_address)) if clt_count == 1: break if self.local_client_buffer: end_head = self.local_client_buffer.find('\n') log(self.lfnr,'%s'%self.local_client_buffer[:end_head]) self.method, self.path, self.protocol = (self.local_client_buffer[:end_head+1]).split() self.local_client_buffer = self.local_client_buffer[end_head+1:] # we can't handle 1.1 persistent connections in proxys, thanks mozilla :( self.protocol = string.replace(self.protocol, 'HTTP/1.1', 'HTTP/1.0') if self.method=='CONNECT': self.method_ssl() elif self.method in ('OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE'): self.method_get() else: self.method_invalid() try: self.target.close() except: pass except Exception as uerr: log(self.lfnr,'__Unexpected Error__: %s'%uerr) def method_invalid(self): log(self.lfnr,'Invalid HTTP Method: %s'%self.method) self.local_client.send('HTTP/1.1 501 Not Implemented\r\n'+'Unknown method: %s\r\n\r\n'%self.method) self.local_client_buffer = '' self.local_client.close() def method_ssl(self): if not self.connect_target(self.path): self.local_client.send('HTTP/1.1 500\r\n\r\n') self.local_client.send('HTTP/1.1 500 %s\r\nProxy: %s\r\n\r\n'%(self.conn_error,version)) return False #self.local_client.send('HTTP/1.1 200 OK\r\n\r\n') self.local_client.send('HTTP/1.1 200 Connection established\r\n'+'Proxy: %s\r\n\r\n'%version) self.local_client_buffer = '' self.handle_select() def method_get(self): j = self.path.find('://') self.scheme = self.path[:j] self.path = self.path[j+3:] i = self.path.find('/') host = self.path[:i] path = self.path[i:] if not self.connect_target(host): self.local_client.send('HTTP/1.1 500\r\n\r\n') self.local_client.send('HTTP/1.1 500 %s\r\nProxy: %s\r\n\r\n'%(self.conn_error,version)) return False s1 = self.target.send('%s %s %s\r\n'%(self.method, path, self.protocol)+self.local_client_buffer) log2(self.lfnr,'SEND_RAW(to %s):%s %s %s\n'%(self.target_addr[0:2],self.method, path, self.protocol)+self.local_client_buffer) log(self.lfnr,'SEND_REQUEST(to %s): %s %s %s\n'%(self.target_addr[0:2], self.method, path, self.protocol)) self.local_client_buffer = '' self.handle_select() def connect_target(self, host): if host.find('[') != -1: j = host.find(']:') if j!=-1: port = int(host[j+2:]) host = host[1:j] else: host = host[1:-1] if self.scheme.lower() == 'ftp': port = 21 else: port = 80 else: i = host.find(':') if i!=-1: port = int(host[i+1:]) host = host[:i] else: if self.scheme.lower() == 'ftp': port = 21 else: port = 80 if self.method=='CONNECT': if port != 443: self.conn_error = 'Connection refused. Port %d not allowed in CONNECT method'%port return False try: addrinfo = socket.getaddrinfo(host, port) except socket.error as msg: log(self.lfnr,"Error resolving %s:%d: %s" % (host, port, msg)) self.conn_error = 'Error resolving %s:%d: %s'%(host, port,msg[1]) return False for (afm, _, _, _, address) in addrinfo: if afm == socket.AF_INET6: self.target = socket.socket(socket.AF_INET6) self.target_addr = address self.af = 'IPv6' break elif afm == socket.AF_INET: self.target = socket.socket(socket.AF_INET) self.target_addr = address self.af = 'IPv4' else: log(self.lfnr,"EEERRRRRRRRRRRRRRRRRRRR\n") self.target.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0) self.target.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 10)) try: self.target.connect(self.target_addr) except socket.error as msg: log(self.lfnr,"%s - Error connecting %s:%d (Addr: %s): %s" % (self.af, host, port, self.target_addr, msg)) self.conn_error = 'Connection failed: %s'%msg[1] return False else: log(self.lfnr,"%s - connect to %s:%d (%s)" % (self.af, host, port, self.target_addr[0:2])) return True def handle_select(self): self.soc_pair = [self.local_client, self.target] count = 0 rf = 'undefined' while 1: count += 1 (recv_data, _, error) = select(self.soc_pair, [], self.soc_pair, 2) if recv_data: cnt = 0 for i in recv_data: cnt +=1 if i is self.local_client: rf = self.local_client_address else: rf = self.target_addr try: data = i.recv(buflen) except socket.error as err: log(self.lfnr,'recv error (%s):%s' % (rf, err)) if err[0] == 104: # connection reset by peer continue log2(self.lfnr,'%d %d select handle from: %s LEN: %d' % (count,cnt,rf,len(data))) if len(data) == 0: continue if i is self.local_client: #self.view_sockopts(self.local_client) if 'HTTP/1.' in data: bs = data.find('\n') log(self.lfnr,'%s - Request from %s: %s'%(self.af,self.local_client_address[0:2],data[:bs-1])) rf = self.local_client_address log2(self.lfnr,'DATA(to %s from %s):%s'% (self.target_addr,rf,data[:260])) out = self.target elif i is self.target: if 'HTTP/1.' in data[0:8]: bs = data[0:256].find('\n') log(self.lfnr,'%s - Answer from %s: %s'%(self.af,self.target_addr[0:2],data[:bs-1])) rf = self.target_addr log2(self.lfnr,'DATA(to %s from %s):%s'% (self.local_client_address,rf,data[:260])) out = self.local_client else: log(self.lfnr,"EEEEEERRRRRROOOORRRRRRRRR: %s"% i) if data: try: sent_byte = out.send(data) except socket.error as err: log(self.lfnr,'send error (%s):%s' % (rf, err)) else: log2(self.lfnr,'Sent %d Bytes Data to %s' % (sent_byte,out.getpeername()[0:2])) count = 0 if error: err = select.error log(self.lfnr,"%s - Error select: %s on %s:%s " % (self.af, err, self.local_client_address, self.target_addr)) if count == 10: break def view_sockopts(self,s): socket_options = [ (getattr(socket, opt), opt) for opt in dir(socket) if opt.startswith('SO_') ] socket_options.sort() for num, opt in socket_options: try: val = s.getsockopt(socket.SOL_SOCKET, num) log2(self.lfnr,'%s(%d) defaults to %d' % (opt, num, val)) except (socket.error) as e: log2(self.lfnr,'%s(%d) can\'t help you out there: %s' % (opt, num, str(e))) # -- Main functions def log(lfnr,msg): logging.warn('[%s] %s'%(lfnr,msg)) def log2(lfnr,msg): if debug: logging.warn('[%s] %s'%(lfnr,msg)) def usage(): print(sys.argv[0]), print(" [-p port] [-s host] [-l logfile] [-d] [-h]") print('') print(" -p - Port to listen on") print(" -s - Host to listen on. If not specified, 0.0.0.0 is used") print(" -l - Path to logfile. If not specified, STDOUT is used") print(" -d - debug messages") print('') def main(host, port): logfile = None try: opts, args = getopt.getopt(sys.argv[1:], "l:hdp:s:", []) except getopt.GetoptError: usage() return 1 for opt, value in opts: if opt == "-p": port = int(value) if opt == "-s": host = value if opt == "-l": logfile = value if opt == "-d": global debug debug = True if opt == "-h": usage() return 0 if not socket.has_ipv6: print("This machine is not IPv6 capable. Exiting.\n") exit(1) logging.basicConfig(format='%(asctime)s %(thread)s: %(message)s', filename=logfile) server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((host, port)) log(lfnr,"Start Server") log(lfnr,"Listen on %s:%d"%(host, port)) server_socket.listen(10) while 1: thread.start_new_thread(ProxyHandler, server_socket.accept()) if __name__ == '__main__': main(listen_host, listen_port)