Пример #1
0
def main():
    addr = config.tun_addr or config.uri_addr
    port = config.tun_port or config.uri_port
    uri = config.uri
    factory = WebSocketServerFactory(uri)
    factory.protocol = WSTunServerProtocol
    factory.server = ''  # hide Server field of handshake HTTP header
    factory.autoPingInterval = 400  # only used to clear half-open connections
    factory.autoPingTimeout = 30
    factory.openHandshakeTimeout = 8  # timeout after TCP established and before succeeded WS handshake

    try:
        server = loop.run_until_complete(
            loop.create_server(factory, addr, port))
    except OSError:
        die('wstan server failed to bind on %s:%d' % (addr, port))
    so = server.sockets[0]
    if len(server.sockets) == 1 and so.family == socket.AF_INET6 and hasattr(
            socket, 'IPPROTO_IPV6'):
        # force user to specify URI in wstan server is a bad design, this try to fix
        # inconvenience in dual stack server
        so.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY,
                      0)  # default 1 in Linux

    loop.set_exception_handler(silent_timeout_err_handler)
    async_(clean_seen_nonce())

    print('wstan server -- listening on %s:%d' % (addr, port))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.close()
        loop.close()
Пример #2
0
def main():
    addr = config.tun_addr or config.uri_addr
    port = config.tun_port or config.uri_port
    uri = config.uri
    factory = WebSocketServerFactory(uri)
    factory.protocol = WSTunServerProtocol
    factory.server = ''  # hide Server field of handshake HTTP header
    factory.autoPingInterval = 400  # only used to clear half-open connections
    factory.autoPingTimeout = 30
    factory.openHandshakeTimeout = 8  # timeout after TCP established and before succeeded WS handshake

    try:
        server = loop.run_until_complete(loop.create_server(factory, addr, port))
    except OSError:
        die('wstan server failed to bind on %s:%d' % (addr, port))
    so = server.sockets[0]
    if len(server.sockets) == 1 and so.family == socket.AF_INET6 and hasattr(socket, 'IPPROTO_IPV6'):
        # force user to specify URI in wstan server is a bad design, this try to fix
        # inconvenience in dual stack server
        so.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)  # default 1 in Linux

    loop.set_exception_handler(silent_timeout_err_handler)
    async_(clean_seen_nonce())

    print('wstan server -- listening on %s:%d' % (addr, port))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.close()
        loop.close()
Пример #3
0
def main():
    addr = config.tun_addr or config.uri_addr
    port = config.tun_port or config.uri_port

    try:
        c = loop.create_server_tfo if config.tfo else loop.create_server
        server = loop.run_until_complete(c(factory, addr, port))
    except OSError:
        die('wstan server failed to bind on %s:%d' % (addr, port))
    so = server.sockets[0]
    if len(server.sockets) == 1 and so.family == socket.AF_INET6 and hasattr(
            socket, 'IPPROTO_IPV6'):
        # force user to specify URI in wstan server is a bad design, this try to fix
        # inconvenience in dual stack server
        so.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY,
                      0)  # default 1 in Linux

    loop.set_exception_handler(silent_timeout_err_handler)
    async_(clean_seen_nonce())

    print('wstan server -- listening on %s:%d' % (addr, port))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.close()
        loop.close()
Пример #4
0
    def onConnect(self, request):
        # ----- init decryptor -----
        if not config.tun_ssl:
            if config.compatible:
                cookie = request.headers['cookie']
                if cookie.count(';') > 0:
                    raise ConnectionDeny(400)
                if not cookie.startswith(config.cookie_key + '='):
                    raise ConnectionDeny(400)
                nonceB64 = cookie.lstrip(config.cookie_key + '=')
            else:
                nonceB64 = request.headers['sec-websocket-key']
            try:
                nonce = base64.b64decode(nonceB64)
                self.initCipher(nonce, decryptor=True)
            except Exception as e:
                logging.error('failed to initialize cipher: %s' % e)
                raise ConnectionDeny(400)
        else:
            nonceB64 = nonce = None  # for decrypting

        # ----- extract header -----
        path = self.http_request_path
        try:
            if path.startswith(factory.path):
                path = path[len(factory.path):]
            dat = base64.urlsafe_b64decode(
                path[1:] if path.startswith('/') else path)
            cmd = ord(self.decrypt(dat[:1]))
            addr, port, remainData, timestamp = self.parseRelayHeader(dat)
            if cmd != self.CMD_REQ:
                raise ValueError('wrong command %s' % cmd)
        except (ValueError, Base64Error) as e:
            logging.error('invalid request: %s (from %s), path: %s' %
                          (e, self.peer, path))
            raise ConnectionDeny(400)

        if not config.tun_ssl:
            # filter replay attack
            seen = seenNonceByTime[timestamp // 10]
            if nonce in seen:
                logging.warning('replay attack detected (from %s)' % self.peer)
                raise ConnectionDeny(400)
            seen.add(nonce)

            if config.compatible:
                # avoid generating a new random nonce for encrypting, and client will do same
                # calculating to get this nonce
                encNonce = get_sha1(nonce)[:16]
            else:
                # repeat calculation in websocket library so that key in WS handshake reply
                # is the same as this one
                encNonce = get_sha1(
                    nonceB64.encode() +
                    b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")[:16]
            self.initCipher(encNonce, encryptor=True)

        self.tunOpen.set_result(None)
        self.connectTargetTask = async_(
            self.connectTarget(addr, port, remainData))
Пример #5
0
    def onMessage(self, dat, isBinary):
        if not isBinary:
            logging.error('non binary ws message received (from %s)' % self.clientInfo)
            return self.sendClose(3000)

        cmd = ord(self.decrypt(dat[:1]))
        if cmd == self.CMD_RST:
            try:
                msg = self.parseResetMessage(dat)
            except ValueError as e:
                logging.error('invalid reset message: %s (from %s)' % (e, self.clientInfo))
                return self.sendClose(3000)
            if not msg.startswith('  '):
                logging.info('tunnel abnormal reset: %s' % msg)
            self.onResetTunnel()
        elif cmd == self.CMD_REQ:
            try:
                if self.tunState != self.TUN_STATE_IDLE:
                    raise Exception('reset received when not idle')
                addr, port, remainData, __ = self.parseRelayHeader(dat)
            except Exception as e:
                logging.error('invalid request in reused tun: %s (from %s)' % (e, self.clientInfo))
                return self.sendClose(3000)
            self.connectTargetTask = async_(self.connectTarget(addr, port, remainData))
        elif cmd == self.CMD_DAT:
            dat = self.decrypt(dat[1:])
            if self.tunState == self.TUN_STATE_RESETTING:
                return
            if self.connectTargetTask:
                self._dataToTarget += dat
                return
            self._writer.write(dat)
        else:
            logging.error('wrong command: %s (from %s)' % (cmd, self.clientInfo))
            self.sendClose(3000)
Пример #6
0
    def startProxy(cls, addrHeader, dat, reader, writer):
        canErr = can_return_error_page(dat)

        if cls.pool:
            logging.debug('reuse tunnel from pool (total %s)' % len(cls.pool))
            tun = cls.pool[0]
            tun.checkTimeoutTask.cancel()
            tun.checkTimeoutTask = None
            tun.tryRemoveFromPool()
            tun.setAutoPing(cls.TUN_AUTO_PING_INTERVAL,
                            cls.TUN_AUTO_PING_TIMEOUT)
            tun.sendMessage(tun.makeRelayHeader(addrHeader, dat), True)
        else:
            try:
                sock = None
                if config.proxy:
                    sock = yield from setup_http_tunnel()

                tun = (yield from loop.create_connection(
                    factory,
                    None if sock else config.uri_addr,
                    None if sock else config.uri_port,
                    server_hostname=config.uri_addr
                    if config.tun_ssl else None,
                    sock=sock,
                    ssl=config.tun_ssl))[1]
                # lower latency by sending relay header and data in ws handshake
                tun.customUriPath = '/' + base64.urlsafe_b64encode(
                    tun.makeRelayHeader(addrHeader, dat)).decode()
                async_(tun.restartHandshake())
                yield from wait_for(tun.tunOpen, tun.openHandshakeTimeout)
            except Exception as e:
                if isinstance(e,
                              (asyncio.TimeoutError, asyncio.CancelledError)):
                    # sometimes reason can be None in extremely poor network
                    msg = tun.wasNotCleanReason or ''
                else:
                    msg = str(e)
                msg = translate_err_msg(msg)
                logging.error("can't connect to server: %s" % msg)
                if canErr:
                    writer.write(
                        gen_error_page("can't connect to wstan server", msg))
                return writer.close()
        tun.canReturnErrorPage = canErr
        tun.setProxy(reader, writer)
Пример #7
0
 def addToPool(self):
     assert self.tunState == self.TUN_STATE_IDLE
     if len(self.pool) >= self.POOL_MAX_SIZE:
         self.sendClose(1000)
     else:
         assert not self.checkTimeoutTask
         self.checkTimeoutTask = async_(self._checkTimeout())
         self.inPool = True
         self.setAutoPing(self.POOL_AUTO_PING_INTERVAL, self.POOL_AUTO_PING_TIMEOUT)
         self.pool.append(self)
Пример #8
0
 def addToPool(self):
     assert self.tunState == self.TUN_STATE_IDLE
     if len(self.pool) >= self.POOL_MAX_SIZE:
         self.sendClose(1000)
     else:
         assert not self.checkTimeoutTask
         self.checkTimeoutTask = async_(self._checkTimeout())
         self.inPool = True
         self.setAutoPing(self.POOL_AUTO_PING_INTERVAL, self.POOL_AUTO_PING_TIMEOUT)
         self.pool.append(self)
Пример #9
0
    def onConnect(self, request):
        self.clientInfo = '{0}:{1}'.format(*self.transport.get_extra_info('peername'))
        # ----- init decryptor -----
        if not config.tun_ssl:
            if config.compatible:
                cookie = request.headers['cookie']
                if cookie.count(';') > 0:
                    raise ConnectionDeny(400)
                if not cookie.startswith(config.cookie_key + '='):
                    raise ConnectionDeny(400)
                nonceB64 = cookie.lstrip(config.cookie_key + '=')
            else:
                nonceB64 = request.headers['sec-websocket-key']
            try:
                nonce = base64.b64decode(nonceB64)
                self.initCipher(nonce, decryptor=True)
            except Exception as e:
                logging.error('failed to initialize cipher: %s' % e)
                raise ConnectionDeny(400)
        else:
            nonceB64 = nonce = None  # for decrypting

        # ----- extract header -----
        try:
            dat = base64.urlsafe_b64decode(self.http_request_path[1:])
            cmd = ord(self.decrypt(dat[:1]))
            addr, port, remainData, timestamp = self.parseRelayHeader(dat)
            if cmd != self.CMD_REQ:
                raise ValueError('wrong command %s' % cmd)
        except (ValueError, Base64Error) as e:
            logging.error('invalid request: %s (from %s), path: %s' %
                          (e, self.clientInfo, self.http_request_path))
            raise ConnectionDeny(400)

        if not config.tun_ssl:
            # filter replay attack
            seen = seenNonceByTime[timestamp // 10]
            if nonce in seen:
                logging.warning('replay attack detected (from %s)' % self.clientInfo)
                raise ConnectionDeny(400)
            seen.add(nonce)

            if config.compatible:
                # avoid generating a new random nonce for encrypting, and client will do same
                # calculating to get this nonce
                encNonce = get_sha1(nonce)[:16]
            else:
                # repeat calculation in websocket library so that key in WS handshake reply
                # is the same as this one
                encNonce = get_sha1(nonceB64.encode() + b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")[:16]
            self.initCipher(encNonce, encryptor=True)

        self.tunOpen.set_result(None)
        self.connectTargetTask = async_(self.connectTarget(addr, port, remainData))
Пример #10
0
    def startProxy(cls, addrHeader, dat, reader, writer):
        if not dat:
            logging.debug('startProxy with no data')
        canErr = can_return_error_page(dat)

        if cls.pool:
            logging.debug('reuse tunnel from pool (total %s)' % len(cls.pool))
            tun = cls.pool[0]
            tun.checkTimeoutTask.cancel()
            tun.checkTimeoutTask = None
            tun.tryRemoveFromPool()
            tun.setAutoPing(cls.TUN_AUTO_PING_INTERVAL, cls.TUN_AUTO_PING_TIMEOUT)
            tun.canReturnErrorPage = canErr
            tun.setProxy(reader, writer)
            tun.sendMessage(tun.makeRelayHeader(addrHeader, dat), True)
        else:
            try:
                sock = None
                if config.proxy:
                    sock = yield from setup_http_tunnel()

                tun = (yield from loop.create_connection(
                    factory, None if sock else config.uri_addr, None if sock else config.uri_port,
                    server_hostname=config.uri_addr if config.tun_ssl else None,
                    sock=sock, ssl=config.tun_ssl))[1]
                # Lower latency by sending relay header and data in ws handshake
                tun.customUriPath = factory.path + base64.urlsafe_b64encode(
                        tun.makeRelayHeader(addrHeader, dat)).decode()
                tun.canReturnErrorPage = canErr
                tun.setProxy(reader, writer, startPushLoop=False)
                async_(tun.restartHandshake())
                # Data may arrive before setProxy if wait for onOpen here
                # and then set proxy.
            except Exception as e:
                msg = translate_err_msg(str(e))
                logging.error("can't connect to server: %s" % msg)
                if canErr:
                    writer.write(gen_error_page("can't connect to wstan server", msg))
                return writer.close()
Пример #11
0
def http_proxy_handler(dat, reader, writer):
    # get request line and header
    while True:  # the line is not likely to be that long
        if b'\r\n\r\n' in dat:
            break
        r = yield from reader.read(1024)
        if not r:
            return writer.close()
        dat += r
    rl_end = dat.find(b'\r\n')
    req_line, rest_dat = dat[:rl_end], dat[rl_end:]

    method, url, ver = req_line.split()
    if method == b'CONNECT':  # e.g. g.cn:443
        host, port = url.split(b':')
        port = int(port)
    elif url.startswith(b'http'):  # e.g. http://g.cn/aa
        parsed = urlparse.urlparse(url)
        path, host, port = parsed.path, parsed.hostname, parsed.port or 80
    else:
        writer.write(gen_log_view_page())
        return writer.close()
    addr_header = make_socks_addr(host, port)

    if method == b'CONNECT':
        writer.write(b'HTTP/1.1 200 Connection Established\r\n\r\n')
        try:
            dat = yield from wait_for(reader.read(2048), 0.01)
            if not dat:
                return writer.close()
        except asyncio.TimeoutError:
            dat = None
    else:
        dat = method + b' ' + path + b' ' + ver + rest_dat
        dat = http_die_soon(dat)  # let target know keep-alive is not supported

    logging.info('requesting %s:%d' % (host.decode(), port))
    async_(WSTunClientProtocol.startProxy(addr_header, dat, reader, writer))
Пример #12
0
    def startProxy(cls, addrHeader, dat, reader, writer):
        canErr = can_return_error_page(dat)

        if cls.pool:
            logging.debug('reuse tunnel from pool (total %s)' % len(cls.pool))
            tun = cls.pool[0]
            tun.checkTimeoutTask.cancel()
            tun.checkTimeoutTask = None
            tun.tryRemoveFromPool()
            tun.setAutoPing(cls.TUN_AUTO_PING_INTERVAL, cls.TUN_AUTO_PING_TIMEOUT)
            tun.sendMessage(tun.makeRelayHeader(addrHeader, dat), True)
        else:
            try:
                sock = None
                if config.proxy:
                    sock = yield from setup_http_tunnel()

                tun = (yield from loop.create_connection(
                    factory, None if sock else config.uri_addr, None if sock else config.uri_port,
                    server_hostname=config.uri_addr if config.tun_ssl else None,
                    sock=sock, ssl=config.tun_ssl))[1]
                # lower latency by sending relay header and data in ws handshake
                tun.customUriPath = '/' + base64.urlsafe_b64encode(tun.makeRelayHeader(addrHeader, dat)).decode()
                async_(tun.restartHandshake())
                yield from wait_for(tun.tunOpen, tun.openHandshakeTimeout)
            except Exception as e:
                if isinstance(e, (asyncio.TimeoutError, asyncio.CancelledError)):
                    # sometimes reason can be None in extremely poor network
                    msg = tun.wasNotCleanReason or ''
                else:
                    msg = str(e)
                msg = translate_err_msg(msg)
                logging.error("can't connect to server: %s" % msg)
                if canErr:
                    writer.write(gen_error_page("can't connect to wstan server", msg))
                return writer.close()
        tun.canReturnErrorPage = canErr
        tun.setProxy(reader, writer)
Пример #13
0
def socks5_tcp_handler(dat, reader, writer):
    # handle auth method selection
    if len(dat) < 2 or len(dat) != dat[1] + 2:
        logging.warning('bad SOCKS v5 request')
        return writer.close()
    writer.write(b'\x05\x00')  # \x00 == NO AUTHENTICATION REQUIRED

    # handle relay request
    dat = yield from reader.read(262)
    try:
        cmd, addr_header = dat[1], dat[2:]
        target_addr, target_port = parse_socks_addr(addr_header)
    except (ValueError, IndexError):
        logging.warning('invalid SOCKS v5 relay request')
        return writer.close()
    if cmd != 0x01:  # CONNECT
        writer.write(b'\x05\x07\x00\x01' +
                     b'\x00' * 6)  # \x07 == COMMAND NOT SUPPORTED
        return writer.close()

    # Delay can be lowered (of a round-trip) by accepting request before connected to target.
    # But SOCKS client can't get real reason when error happens (not a big problem, Firefox always
    # display connection reset error). Dirty solution: generate a HTML page when a HTTP request failed
    writer.write(b'\x05\x00\x00\x01' + b'\x01' * 6)  # \x00 == SUCCEEDED
    try:
        dat = yield from wait_for(reader.read(2048), 0.02)
        if not dat:
            return writer.close()
    except asyncio.TimeoutError:
        # 20ms passed and no data received, rare but legal behavior.
        # timeout may always happen if set to 10ms, and enable asyncio library debug mode will "fix" it
        # e.g. Old SSH client will wait for server after conn established
        dat = None

    async_(
        WSTunClientProtocol.openTunnel((target_addr, target_port), dat, reader,
                                       writer))
Пример #14
0
    def onMessage(self, dat, isBinary):
        if not isBinary:
            logging.error('non binary ws message received (from %s)' % self.clientInfo)
            return self.sendClose(3000)

        cmd = ord(self.decrypt(dat[:1]))
        if cmd == self.CMD_RST:
            try:
                msg = self.parseResetMessage(dat)
            except ValueError as e:
                logging.error('invalid reset message: %s (from %s)' % (e, self.clientInfo))
                return self.sendClose(3000)
            if not msg.startswith('  '):
                logging.info('tunnel abnormal reset: %s' % msg)
            self.onResetTunnel()
        elif cmd == self.CMD_REQ:
            try:
                if self.tunState != self.TUN_STATE_IDLE:
                    raise Exception('reset received when not idle')
                addr, port, remainData, __ = self.parseRelayHeader(dat)
            except Exception as e:
                logging.error('invalid request in reused tun: %s (from %s)' % (e, self.clientInfo))
                return self.sendClose(3000)
            self.connectTargetTask = async_(self.connectTarget(addr, port, remainData))
        elif cmd == self.CMD_DAT:
            dat = self.decrypt(dat[1:])
            if self.tunState == self.TUN_STATE_RESETTING:
                return
            if self.connectTargetTask:
                logging.debug('data received when connectTargetTask running')
                # will order of messages be changed by waiting?
                yield from wait_for(self.connectTargetTask, None)
            self._writer.write(dat)
        else:
            logging.error('wrong command: %s (from %s)' % (cmd, self.clientInfo))
            self.sendClose(3000)
Пример #15
0
    def onOpen(self):
        self.tunOpen.set_result(None)
        now = time.time()
        self.lastIdleTime = now

        # measure RTT
        rtt = now - self.handshakeSentTime
        if WSTunClientProtocol.rtt is None:
            WSTunClientProtocol.rtt = rtt
        else:
            WSTunClientProtocol.rtt = 0.8 * WSTunClientProtocol.rtt + 0.2 * rtt

        assert not self._pushToTunTask
        self._pushToTunTask = async_(self._pushToTunnelLoop())
        self.setAutoPing(self.TUN_AUTO_PING_INTERVAL, self.TUN_AUTO_PING_TIMEOUT)
        if not config.debug:
            self.customUriPath = None  # save memory
        if not config.tun_ssl:
            if config.compatible:
                nonce = get_sha1(base64.b64decode(self.customWsKey))[:16]
            else:
                # SHA-1 has 20 bytes
                nonce = base64.b64decode(self.http_headers['sec-websocket-accept'])[:16]
            self.initCipher(nonce, decryptor=True)
Пример #16
0
 def _onMessageEnd(self):
     res = self.onMessageEnd()
     if yields(res):
         asyncio.async_(res)
Пример #17
0
 def _onMessage(self, payload, isBinary):
     res = self.onMessage(payload, isBinary)
     if yields(res):
         asyncio.async_(res)
Пример #18
0
 def setProxy(self, reader, writer):
     self.tunState = self.TUN_STATE_USING
     self._reader, self._writer = reader, writer
     self._pushToTunTask = async_(self._pushToTunnelLoop())
Пример #19
0
 def _onConnect(self, response):
     res = self.onConnect(response)
     if yields(res):
         asyncio.async_(res)
Пример #20
0
 def _onMessageBegin(self, isBinary):
     res = self.onMessageBegin(isBinary)
     if yields(res):
         asyncio.async_(res)
Пример #21
0
 def startPushToTunLoop(self):
     assert not self._pushToTunTask
     self._pushToTunTask = async_(self._pushToTunnelLoop())
     self._pushToTunTask.add_done_callback(_on_pushToTunTaskDone)
Пример #22
0
 def _onMessageFrame(self, payload):
     res = self.onMessageFrame(payload)
     if yields(res):
         asyncio.async_(res)
Пример #23
0
 def _onMessageEnd(self):
     res = self.onMessageEnd()
     if yields(res):
         asyncio.async_(res)
Пример #24
0
 def setProxy(self, reader, writer):
     self.tunState = self.TUN_STATE_USING
     self._reader, self._writer = reader, writer
     self._pushToTunTask = async_(self._pushToTunnelLoop())
Пример #25
0
 def _onMessageFrameBegin(self, length):
     res = self.onMessageFrameBegin(length)
     if yields(res):
         asyncio.async_(res)
Пример #26
0
 def _onPong(self, payload):
     res = self.onPong(payload)
     if yields(res):
         asyncio.async_(res)
Пример #27
0
 def _onClose(self, wasClean, code, reason):
     res = self.onClose(wasClean, code, reason)
     if yields(res):
         asyncio.async_(res)
Пример #28
0
    def openTunnel(cls, target, dat, reader, writer, retryCount=0):
        logging.info('requesting %s:%d' % target)

        if not dat:
            logging.debug('openTunnel with no data')
        canErr = can_return_error_page(dat)

        if cls.pool:  # reuse from pool
            logging.debug('reuse tunnel from pool (total %s)' % len(cls.pool))
            tun = cls.pool[0]
            tun.checkTimeoutTask.cancel()
            tun.checkTimeoutTask = None
            tun.tryRemoveFromPool()
            tun.setAutoPing(cls.TUN_AUTO_PING_INTERVAL,
                            cls.TUN_AUTO_PING_TIMEOUT)
            tun.canReturnErrorPage = canErr
            tun.setProxy(reader, writer)
            tun.sendMessage(tun.makeRelayHeader(target, dat), True)
            return

        # new tunnel
        try:
            if retryCount >= cls.MAX_RETRY_COUNT:
                raise ConnectionResetError(
                    'run into tcp reset, all retries failed')

            sock = None
            if config.proxy:
                sock = yield from setup_http_tunnel()

            tun = factory()
            # Lower latency by sending relay header and data in ws handshake
            tun.customUriPath = factory.path + base64.urlsafe_b64encode(
                tun.makeRelayHeader(target, dat)).decode()
            tun.canReturnErrorPage = canErr
            # Data may arrive before setProxy if wait for tunOpen here and then set proxy.
            tun.setProxy(reader, writer,
                         startPushLoop=False)  # push loop will start in onOpen

            if config.tfo:
                assert not config.proxy and not config.tun_ssl
                tun.noSendHandshake = True
                tun.startHandshake()
                # tfo is meaningless if handshake data can't fit into TCP SYN packet
                # switch back to normal sock_connect just in case my Windows tfo extension has bug
                tfoDat = tun.http_request_data if len(
                    tun.http_request_data) <= 1400 else None
                sock = yield from my_sock_connect(config.uri_addr,
                                                  config.uri_port,
                                                  tfo_dat=tfoDat)
                # it will return after SYN,ACK received regardless of TFO
                if not tfoDat:
                    loop.sock_sendall(sock, tun.http_request_data)

            yield from loop.create_connection(
                lambda: tun,
                None if sock else config.uri_addr,
                None if sock else config.uri_port,
                server_hostname=config.uri_addr if config.tun_ssl else None,
                sock=sock,
                ssl=config.tun_ssl)
        except Exception as e:
            msg = translate_err_msg(str(e))
            dest = 'proxy' if config.proxy and not sock else 'wstan server'
            logging.error("can't connect to %s: %s" % (dest, msg))
            if canErr:
                writer.write(gen_error_page("can't connect to " + dest, msg))
            return writer.close()

        try:
            yield from wait_for(tun.tunOpen, None)
        except CancelledError:
            # sometimes reason can be None in extremely poor network
            msg = tun.wasNotCleanReason or ''

            if isinstance(tun.connLostReason, ConnectionResetError):
                # GFW random reset HTTP stream it can't recognize, just retry
                return async_(
                    cls.openTunnel(target, dat, reader, writer,
                                   retryCount + 1))

            msg = translate_err_msg(msg)
            logging.error("can't connect to server: %s" % msg)
            if tun.wasNotCleanReason and tun.canReturnErrorPage:  # write before closing writer
                writer.write(
                    gen_error_page("can't connect to wstan server", msg))
            return writer.close()

        if retryCount > 0:
            logging.debug('tcp reset happen, retried %d times' % retryCount)
Пример #29
0
 def _onMessageFrame(self, payload):
     res = self.onMessageFrame(payload)
     if yields(res):
         asyncio.async_(res)
Пример #30
0
 def _onMessage(self, payload, isBinary):
     res = self.onMessage(payload, isBinary)
     if yields(res):
         asyncio.async_(res)
Пример #31
0
 def _onMessageFrameBegin(self, length):
     res = self.onMessageFrameBegin(length)
     if yields(res):
         asyncio.async_(res)
Пример #32
0
 def _onPong(self, payload):
     res = self.onPong(payload)
     if yields(res):
         asyncio.async_(res)
Пример #33
0
 def _onOpen(self):
     res = self.onOpen()
     if yields(res):
         asyncio.async_(res)
Пример #34
0
 def _onClose(self, wasClean, code, reason):
     res = self.onClose(wasClean, code, reason)
     if yields(res):
         asyncio.async_(res)
Пример #35
0
 def _onOpen(self):
     res = self.onOpen()
     if yields(res):
         asyncio.async_(res)
Пример #36
0
 def _onConnect(self, response):
     res = self.onConnect(response)
     if yields(res):
         asyncio.async_(res)
Пример #37
0
 def _onMessageBegin(self, isBinary):
     res = self.onMessageBegin(isBinary)
     if yields(res):
         asyncio.async_(res)
Пример #38
0
 def setProxy(self, reader, writer, startPushLoop=True):
     self.tunState = self.TUN_STATE_USING
     self._reader, self._writer = reader, writer
     if startPushLoop:
         self._pushToTunTask = async_(self._pushToTunnelLoop())