Post Reply 
Another Filtering Proxy
Jan. 25, 2016, 04:37 PM (This post was last modified: Jan. 25, 2016 04:52 PM by cattleyavns.)
Post: #34
RE: Another Filtering Proxy
I also want to report another bug, we currently don't have websocket support, I think these days, websocket became very important, especially online remote desktop service like this, without websocket support we cannot control or handshake with remote desktop server: https://dpetechnet.cloudapp.net/Home.aspx

Test: http://websocket.org/echo.html

I tried to implement websocket by editing ProxyTool.py but I cannot make it work, if AFProxy tries to handshake a insecure websocket connect, it will simply fail, only secure websocket works, AFProxy can handshare with websocket server, but cannot send and receive packets:

Code:
from http.server import SimpleHTTPRequestHandler
import struct
from base64 import b64encode
from hashlib import sha1
from email.message import Message
from io import StringIO
import errno, socket #for socket exceptions
import threading



class ProxyRequestHandler(BaseHTTPRequestHandler):
    """RequestHandler with do_CONNECT method defined
    """
    server_version = "%s/%s" % (_name, __version__)
    # do_CONNECT() will set self.ssltunnel to override this
    ssltunnel = False
    # Override default value 'HTTP/1.0'
    protocol_version = 'HTTP/1.1'
    
    _ws_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
    _opcode_continu = 0x0
    _opcode_text = 0x1
    _opcode_binary = 0x2
    _opcode_close = 0x8
    _opcode_ping = 0x9
    _opcode_pong = 0xa

    mutex = threading.Lock()

    def on_ws_message(self, message):
        if message is None:
            message = b''
        # echo message back to client
        #print(dir(self))
        #print(self.headers)
        #self.wfile.write(message)
        self.wfile.write(bytes(message.encode('utf-8')))
        self.log_message('websocket received "%s"',str(message))
        #self.close_connection = 1
    def on_ws_connected(self):
        self.log_message('%s','websocket connected')
    def on_ws_closed(self):
        self.log_message('%s','websocket closed')

    def send_message(self, message):
        self._send_message(self._opcode_text, message)

    def setup(self):
        SimpleHTTPRequestHandler.setup(self)
        self.connected = False

    def finish(self):
        #needed when wfile is used, or when self.close_connection is not used
        # #
        # #catch errors in SimpleHTTPRequestHandler.finish() after socket disappeared
        # #due to loss of network connection
        try:
            SimpleHTTPRequestHandler.finish(self)
        except (socket.error, TypeError) as err:
            self.log_message("finish(): Exception: in SimpleHTTPRequestHandler.finish(): %s" % str(err.args))

    def handle(self):
        #needed when wfile is used, or when self.close_connection is not used
        # #
        # #catch errors in SimpleHTTPRequestHandler.handle() after socket disappeared
        # #due to loss of network connection
        try:
            SimpleHTTPRequestHandler.handle(self)
        except (socket.error, TypeError) as err:
            self.log_message("handle(): Exception: in SimpleHTTPRequestHandler.handle(): %s" % str(err.args))

    def checkAuthentication(self):
        auth = self.headers.get('Authorization')
        if auth != "Basic %s" % self.server.auth:
            self.send_response(401)
            self.send_header("WWW-Authenticate", 'Basic realm="Plugwise"')
            self.end_headers();
            return False
        return True

    def _read_messages(self):
        while self.connected == True:
            try:
                self._read_next_message()
            except (socket.error, WebSocketError) as e:
                #websocket content error, time-out or disconnect.
                self.log_message("RCV: Close connection: Socket Error %s" % str(e.args))
                self._ws_close()
            except Exception as err:
                #unexpected error in websocket connection.
                self.log_error("RCV: Exception: in _read_messages: %s" % str(err.args))
                self._ws_close()

    def _read_next_message(self):
        #self.rfile.read(n) is blocking.
        #it returns however immediately when the socket is closed.
        try:
            #print(self.rfile.read(1))
            self.opcode = ord(self.rfile.read(1)) & 0x0F
            length = ord(self.rfile.read(1)) & 0x7F
            print('length %s opcode %s' %(length, self.opcode))
            if length == 126:
                length = struct.unpack(">H", self.rfile.read(2))[0]
            elif length == 127:
                length = struct.unpack(">Q", self.rfile.read(8))[0]
            masks = [ord(chr(byte)) for byte in self.rfile.read(4)]
            print(masks)
            decoded = ""
            for char in self.rfile.read(length):
                #print(chr(ord(chr(char)) ^ masks[len(decoded) % 4]))
                decoded += chr(ord(chr(char)) ^ masks[len(decoded) % 4])
            #print(decoded)
            self._on_message(decoded)
        except (struct.error, TypeError) as e:
            #catch exceptions from ord() and struct.unpack()
            if self.connected:
                raise WebSocketError("Websocket read aborted while listening")
            else:
                #the socket was closed while waiting for input
                self.log_error("RCV: _read_next_message aborted after closed connection")
                pass

    def _send_message(self, opcode, message):
        print('123')
        try:
                #use of self.wfile.write gives socket exception after socket is closed. Avoid.
            self.wfile.write(chr(0x80 + opcode))
            print('self.wfile.write(chr(0x80 + opcode)) %s' %(chr(0x80 + opcode)))
            length = len(message)
            if length <= 125:
                self.wfile.write(chr(length))
                print('self.wfile.write(chr(length)) %s' %(chr(length)))
            elif length >= 126 and length <= 65535:
                self.wfile.write(chr(126))
                print('self.wfile.write(chr(126)) %s' %(chr(126)))
                self.wfile.write(struct.pack(">H", length))
                print('self.wfile.write(struct.pack(">H", length)) %s' %(struct.pack(">H", length)))
            else:
                self.wfile.write(chr(127))
                print('self.wfile.write(chr(127)) %s' %(chr(127)))
                self.wfile.write(struct.pack(">Q", length))
                print('self.wfile.write(struct.pack(">Q", length)) %s' %(struct.pack(">Q", length)))
            if length > 0:
                self.wfile.write(message)
                print('self.wfile.write(message) %s' %(message))
        except socket.error as e:
            #websocket content error, time-out or disconnect.
            self.log_message("SND: Close connection: Socket Error %s" % str(e.args))
            self._ws_close()
        except Exception as err:
            #unexpected error in websocket connection.
            self.log_error("SND: Exception: in _send_message: %s" % str(err.args))
            self._ws_close()

    def _handshake(self):
        headers=self.headers
        if headers.get("Upgrade", None) != "websocket":
            return
        key = headers['Sec-WebSocket-Key']
        digest = b64encode(sha1(key.encode('utf-8') + self._ws_GUID.encode('utf-8')).digest()).decode('ascii')
        self.send_response(101, 'Switching Protocols')
        self.send_header('Upgrade', 'websocket')
        self.send_header('Connection', 'Upgrade')
        self.send_header('Sec-WebSocket-Accept', str(digest))
        self.send_header('Access-Control-Allow-Credentials', 'true')
        self.send_header('Access-Control-Allow-Headers', 'content-type, authorization, x-websocket-extensions, x-websocket-version, x-websocket-protocol')
        self.send_header('Access-Control-Allow-Origin', 'http://www.websocket.org')
        self.end_headers()
        self.connected = True
        #self.close_connection = 0
        self.on_ws_connected()

    def _ws_close(self):
        #avoid closing a single socket two time for send and receive.
        self.mutex.acquire()
        try:
            if self.connected:
                self.connected = False
                #Terminate BaseHTTPRequestHandler.handle() loop:
                self.close_connection = 1
                #send close and ignore exceptions. An error may already have occurred.
                try:
                    self._send_close()
                except:
                    pass
                self.on_ws_closed()
            else:
                self.log_message("_ws_close websocket in closed state. Ignore.")
                pass
        finally:
            self.mutex.release()

    def _on_message(self, message):
        #self.log_message("_on_message: opcode: %02X msg: %s" % (self.opcode, message))
        #print(self.opcode)
        #print(self._opcode_close)
        # close
        if self.opcode == self._opcode_close:
            self.connected = False
            #Terminate BaseHTTPRequestHandler.handle() loop:
            self.close_connection = 1
            try:
                self._send_close()
            except:
                pass
            self.on_ws_closed()
        # ping
        elif self.opcode == self._opcode_ping:
            _send_message(self._opcode_pong, message)
        # pong
        elif self.opcode == self._opcode_pong:
            pass
        # data
        elif (self.opcode == self._opcode_continu or
                self.opcode == self._opcode_text or
                self.opcode == self._opcode_binary):
            self.on_ws_message(message)

    def _send_close(self):
        #Dedicated _send_close allows for catch all exception handling
        msg = bytearray()
        msg.append(0x80 + self._opcode_close)
        msg.append(0x00)
        self.wfile.write(msg)

Add this line to do_GET:
Code:
                if self.headers.get("Upgrade", None) == "websocket":
                    #print('000000')
                    self._handshake()
                    #This handler is in websocket mode now.
                    #do_GET only returns after client close or socket error.
                    self._read_messages()
                    return



This software has the same problem: https://github.com/google/martian/issues/31
Add Thank You Quote this message in a reply
Post Reply 


Messages In This Thread
Another Filtering Proxy - whenever - Nov. 22, 2014, 09:35 AM
RE: Another Filtering Proxy - whenever - Nov. 29, 2014, 11:24 AM
RE: Another Filtering Proxy - GunGunGun - Nov. 29, 2014, 02:15 PM
RE: Another Filtering Proxy - whenever - Nov. 30, 2014, 12:35 PM
RE: Another Filtering Proxy - GunGunGun - Dec. 03, 2014, 09:07 PM
RE: Another Filtering Proxy - whenever - Dec. 04, 2014, 01:09 AM
RE: Another Filtering Proxy - GunGunGun - Dec. 04, 2014, 02:33 AM
RE: Another Filtering Proxy - GunGunGun - Dec. 04, 2014, 03:27 PM
RE: Another Filtering Proxy - whenever - Dec. 05, 2014, 08:36 AM
RE: Another Filtering Proxy - GunGunGun - Dec. 05, 2014, 09:05 AM
RE: Another Filtering Proxy - whenever - Dec. 08, 2014, 03:30 AM
RE: Another Filtering Proxy - GunGunGun - Dec. 08, 2014, 09:09 AM
RE: Another Filtering Proxy - whenever - Dec. 08, 2014, 12:11 PM
RE: Another Filtering Proxy - whenever - Dec. 28, 2014, 10:50 AM
RE: Another Filtering Proxy - cattleyavns - May. 26, 2015, 06:22 AM
RE: Another Filtering Proxy - whenever - May. 27, 2015, 02:26 PM
RE: Another Filtering Proxy - cattleyavns - May. 28, 2015, 04:26 AM
RE: Another Filtering Proxy - whenever - May. 28, 2015, 09:53 AM
RE: Another Filtering Proxy - cattleyavns - May. 29, 2015, 04:07 AM
RE: Another Filtering Proxy - whenever - Jun. 03, 2015, 07:56 AM
RE: Another Filtering Proxy - cattleyavns - Jun. 05, 2015, 12:26 PM
RE: Another Filtering Proxy - whenever - Jul. 19, 2015, 07:32 AM
RE: Another Filtering Proxy - cattleyavns - Jul. 19, 2015, 05:53 PM
RE: Another Filtering Proxy - cattleyavns - Jun. 17, 2015, 09:14 AM
RE: Another Filtering Proxy - cattleyavns - Jul. 01, 2015, 08:55 AM
RE: Another Filtering Proxy - whenever - Jul. 20, 2015, 03:31 AM
RE: Another Filtering Proxy - cattleyavns - Jul. 20, 2015, 06:17 AM
RE: Another Filtering Proxy - cattleyavns - Jan. 05, 2016, 04:53 PM
RE: Another Filtering Proxy - whenever - Jan. 06, 2016, 08:44 AM
RE: Another Filtering Proxy - cattleyavns - Jan. 06, 2016, 07:40 PM
RE: Another Filtering Proxy - whenever - Jan. 07, 2016, 02:25 AM
RE: Another Filtering Proxy - cattleyavns - Jan. 07, 2016, 08:41 AM
RE: Another Filtering Proxy - cattleyavns - Jan. 10, 2016, 07:27 AM
RE: Another Filtering Proxy - cattleyavns - Jan. 25, 2016 04:37 PM
RE: Another Filtering Proxy - whenever - May. 16, 2016, 08:42 AM

Forum Jump: