Newer
Older
import threading
import socket, array
import select
import logging
Chris Hines
committed
failthresh = 10
AUTHTIMEOUT = 60
MAXBUFF = 8192
MAXHEADERS = 8192
def __init__(self,socket):
super().__init__()
self.csock = socket
self.ssock = None
self.authtok = None
self._timeout = 10
def inittws(self,closed):
""" Read from the client socket until one of
1) We can verify the request based on headers
2) We hit a timeout (i.e. no more headers will be sent and the client is waiting for a response)
If we can verify
open a new socket to the server and return this
"""
bytessofar = 0
header=bytearray(TWSProxy.MAXHEADERS)
keepreading = True
Chris Hines
committed
initcount=0
Chris Hines
committed
initcount=initcount+1
r,w,e = select.select([self.csock],[],[],5)
if len(r) > 0:
partial = self.csock.recv(TWSProxy.MAXBUFF)
header[bytessofar:bytessofar+len(partial)] = partial
bytessofar = bytessofar + len(partial)
Chris Hines
committed
port = TWSProxy.verifyauth(header[0:bytessofar])
if port is not None:
keepreading = False
else:
Chris Hines
committed
port = TWSProxy.verifyauth(header[0:bytessofar])
if port is None:
logger.error('select returned no new bytes, verification failed, headers are {}'.format(header[0:bytessofar]))
Chris Hines
committed
if initcount > failthresh:
logger.error('Exceeded threhold, terminating proxy after {} bytes'.format(bytessofar))
Chris Hines
committed
keepreading = False
logger.debug('authenticated connection {}'.format(self.csock))
self.ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ssock.setblocking(True)
try:
self.ssock.connect(('127.0.0.1',port))
if bytessofar > 0:
return (header,bytessofar)
#TWSProxy.reliablesend(self.ssock,header[0:bytessofar],bytessofar)
logger.error('Couldn\'t open connection to the SSH Tunnel')
self.ssock.close()
self.csock.close()
closed.set()
logger.error('Failed to authenticate to the SSH Tunnel')
import logging
logger = logging.getLogger()
initshutdown = threading.Event()
initshutdown.clear()
(header, bytessofar) = self.inittws(initshutdown)
t1 = None
t2 = None
if not initshutdown.isSet():
TWSProxy.reliablesend(self.ssock,header[0:bytessofar],bytessofar)
# TWSProxy.twosocks(self.csock,self.ssock,initshutdown)
Chris Hines
committed
t1 = threading.Thread(target=TWSProxy.sockcopy, args=(self.ssock, self.csock, initshutdown),name='s2c')
t2 = threading.Thread(target=TWSProxy.sockcopy, args=(self.csock, self.ssock, initshutdown),name='c2s')
t1.start()
t2.start()
t1.join()
t2.join()
if self.ssock is not None:
self.ssock.close()
if self.csock is not None:
self.csock.close()
Chris Hines
committed
@staticmethod
def verifyauth(header):
Chris Hines
committed
# We are looking for either
# 1. An Authentication: token ... header that we can map to an ssh session
# 2. the string token=.... (usually appearing as a url query parameter)
# 3. A cookie called twsproxyauth that we can make to an ssh sesssion
token_formats = [ b'Authorization: token (?P<authtok>\w+)[\W|$]',
Chris Hines
committed
b'token=(?P<authtok>\w+)[&|\W|$]',
b'twsproxyauth=(?P<authtok>\w+)[\W|$]']
for token in token_formats:
m = re.search(token,header)
if m:
Chris Hines
committed
authtok = m.groupdict()['authtok'].rstrip()
s = requests.Session()
url = TES+'tunnelstat/'+authtok.decode()
try:
r = s.get(url)
port = r.json()
except:
raise Exception('unable to get a port number for the authtok {}'.format(r.text))
if port is not None:
return port
except Exception as e:
import traceback
logger.error('Exception')
logger.error(e)
logger.error(traceback.format_exc())
raise e
return None
@staticmethod
def reliablesend(socket,buff,msglength):
totalsent = 0
while totalsent < msglength:
sent = socket.send(buff[totalsent:])
if sent == 0:
raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
@staticmethod
Chris Hines
committed
def sockcopy(src,dest,initshutdown):
shuttype = socket.SHUT_RD
import threading
logger = logging.getLogger()
closed = False
name = threading.current_thread().name
r,w,e = select.select([src],[],[],TWSProxy.TIMEOUT)
buff = None
msglength = -1
try:
buff = src.recv(TWSProxy.MAXBUFF)
if buff is None:
continue
except ConnectionResetError as e:
continue
except Exception as e:
import traceback
logger.error(traceback.format_exc())
msglength = len(buff)
if msglength > 0:
TWSProxy.reliablesend(dest,buff,msglength)
if msglength == 0:
dest.shutdown(shuttype)
initshutdown.set()
closed = True
Chris Hines
committed
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger()
handler = TimedRotatingFileHandler(filename="/var/log/strudel2/tws.log",when='h',interval=24,backupCount=7)
formatter = logging.Formatter("%(asctime)s %(levelname)s:%(process)s: %(message)s")
handler.setFormatter(formatter)
to_log = "{}\n".format(e)
to_log = to_log + traceback.format_exc()
logging.basicConfig(filename=os.path.expanduser("~/.tws.log"),format="%(asctime)s %(levelname)s:%(process)s: %(message)s")
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
Chris Hines
committed
logger.debug("starting TWS proxy")
if port is None:
try:
port = int(sys.argv[1])
except:
port = 8090
Chris Hines
committed
logger.debug("initialised server object")