123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- #!/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)
|