Exemplo n.º 1
0
    def run_server():
        if 'daemon' in config:
            book.Book.Background()

        def child_handler(signum, _):
            logging.warn('received SIGQUIT, doing graceful shutting down..')
            list(
                map(lambda s: s.close(next_tick=True),
                    tcp_servers + udp_servers))

        signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM),
                      child_handler)

        def int_handler(signum, _):
            book.Book.close()
            sys.exit(1)

        signal.signal(signal.SIGINT, int_handler)

        try:
            loop = eventloop.EventLoop()
            dns_resolver.add_to_loop(loop)
            list(map(lambda s: s.add_to_loop(loop), tcp_servers + udp_servers))

            daemon.set_user(config.get('user', None))
            loop.run()
        except Exception as e:
            book.Book.close()
            shell.print_exception(e)
            sys.exit(1)
Exemplo n.º 2
0
    def run(self):
        events = []
        while not self._stopping:
            asap = False
            try:
                events = self.poll(TIMEOUT_PRECISION)
            except (OSError, IOError) as e:
                if errno_from_exception(e) in (errno.EPIPE, errno.EINTR):
                    # EPIPE: Happens when the client closes the connection
                    # EINTR: Happens when received a signal
                    # handles them as soon as possible
                    asap = True
                    logging.debug('poll:%s', e)
                else:
                    logging.error('poll:%s', e)
                    traceback.print_exc()
                    continue

            for sock, fd, event in events:
                handler = self._fdmap.get(fd, None)
                if handler is not None:
                    handler = handler[1]
                    try:
                        handler.handle_event(sock, fd, event)
                    except (OSError, IOError) as e:
                        shell.print_exception(e)
            now = time.time()
            if asap or now - self._last_time >= TIMEOUT_PRECISION:
                for callback in self._periodic_callbacks:
                    callback()
                self._last_time = now
Exemplo n.º 3
0
 def _on_remote_read(self):
     # handle all remote read events
     data = None
     if self._is_local:
         buf_size = UP_STREAM_BUF_SIZE
     else:
         buf_size = DOWN_STREAM_BUF_SIZE
     try:
         data = self._remote_sock.recv(buf_size)
         # logging.info("recv remote: %s " % colored(md5(data).hexdigest(), 'blue'))
     except (OSError, IOError) as e:
         if eventloop.errno_from_exception(e) in \
                 (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK):
             return
     if not data:
         self.destroy()
         return
     self._update_activity(len(data))
     if self._is_local:
         data = self._cryptor.decrypt(data)
     else:
         # tunnel change start
         if self._tunnel_mode:
             data = self._cryptor_tunnel.decrypt(data)
         # tunnel change end
         data = self._cryptor.encrypt(data)
     try:
         self._write_to_sock(data, self._local_sock)
     except Exception as e:
         shell.print_exception(e)
         if self._config['verbose']:
             traceback.print_exc()
         # TODO use logging when debug completed
         self.destroy()
Exemplo n.º 4
0
 def handle_event(self, sock, fd, event):
     # handle events and dispatch to handlers
     if sock:
         logging.log(shell.VERBOSE_LEVEL, 'fd %d %s', fd,
                     eventloop.EVENT_NAMES.get(event, event))
     if sock == self._server_socket:
         if event & eventloop.POLL_ERR:
             # TODO
             raise Exception('server_socket error')
         try:
             logging.debug('accept')
             conn = self._server_socket.accept()
             TCPRelayHandler(self, self._fd_to_handlers, self._eventloop,
                             conn[0], self._config, self._dns_resolver,
                             self._is_local)
         except (OSError, IOError) as e:
             error_no = eventloop.errno_from_exception(e)
             if error_no in (errno.EAGAIN, errno.EINPROGRESS,
                             errno.EWOULDBLOCK):
                 return
             else:
                 shell.print_exception(e)
                 if self._config['verbose']:
                     traceback.print_exc()
     else:
         if sock:
             handler = self._fd_to_handlers.get(fd, None)
             if handler:
                 handler.handle_event(sock, event)
         else:
             logging.warn('poll removed fd')
Exemplo n.º 5
0
    def _send_control_data(self, data):
        if not self._control_client_addr:
            return

        try:
            self._control_socket.sendto(data, self._control_client_addr)
        except (socket.error, OSError, IOError) as e:
            error_no = eventloop.errno_from_exception(e)
            if error_no in (errno.EAGAIN, errno.EINPROGRESS,
                            errno.EWOULDBLOCK):
                return
            else:
                shell.print_exception(e)
                if self._config['verbose']:
                    traceback.print_exc()
Exemplo n.º 6
0
def parse_response(data):
    try:
        if len(data) >= 12:
            header = parse_header(data)
            if not header:
                return None
            res_id, res_qr, res_tc, res_ra, res_rcode, res_qdcount, \
                res_ancount, res_nscount, res_arcount = header

            qds = []
            ans = []
            offset = 12
            for i in range(0, res_qdcount):
                l, r = parse_record(data, offset, True)
                offset += l
                if r:
                    qds.append(r)
            for i in range(0, res_ancount):
                l, r = parse_record(data, offset)
                offset += l
                if r:
                    ans.append(r)
            for i in range(0, res_nscount):
                l, r = parse_record(data, offset)
                offset += l
            for i in range(0, res_arcount):
                l, r = parse_record(data, offset)
                offset += l
            response = DNSResponse()
            if qds:
                response.hostname = qds[0][0]
            for an in qds:
                response.questions.append((an[1], an[2], an[3]))
            for an in ans:
                response.answers.append((an[1], an[2], an[3]))
            return response
    except Exception as e:
        shell.print_exception(e)
        return None
Exemplo n.º 7
0
 def _write_to_sock(self, data, sock):
     # write data to sock
     # if only some of the data are written, put remaining in the buffer
     # and update the stream to wait for writing
     if not data or not sock:
         return False
     uncomplete = False
     try:
         l = len(data)
         s = sock.send(data)
         if s < l:
             data = data[s:]
             uncomplete = True
     except (OSError, IOError) as e:
         error_no = eventloop.errno_from_exception(e)
         if error_no in (errno.EAGAIN, errno.EINPROGRESS,
                         errno.EWOULDBLOCK):
             uncomplete = True
         else:
             shell.print_exception(e)
             self.destroy()
             return False
     if uncomplete:
         if sock == self._local_sock:
             self._data_to_write_to_local.append(data)
             self._update_stream(STREAM_DOWN, WAIT_STATUS_WRITING)
         elif sock == self._remote_sock:
             self._data_to_write_to_remote.append(data)
             self._update_stream(STREAM_UP, WAIT_STATUS_WRITING)
         else:
             logging.error('write_all_to_sock:unknown socket')
     else:
         if sock == self._local_sock:
             self._update_stream(STREAM_DOWN, WAIT_STATUS_READING)
         elif sock == self._remote_sock:
             self._update_stream(STREAM_UP, WAIT_STATUS_READING)
         else:
             logging.error('write_all_to_sock:unknown socket')
     return True
Exemplo n.º 8
0
    def _handle_server(self):
        server = self._server_socket
        data, r_addr = server.recvfrom(BUF_SIZE)
        key = None
        iv = None
        if not data:
            logging.debug('UDP handle_server: data is empty')
        if self._stat_callback:
            self._stat_callback(self._listen_port, len(data))
        if self._is_local:
            if self._is_tunnel:
                # add ss header to data
                tunnel_remote = self.tunnel_remote
                tunnel_remote_port = self.tunnel_remote_port
                data = common.add_header(tunnel_remote, tunnel_remote_port,
                                         data)
            else:
                frag = common.ord(data[2])
                if frag != 0:
                    logging.warn('UDP drop a message since frag is not 0')
                    return
                else:
                    data = data[3:]
        else:
            # decrypt data
            try:
                data, key, iv = cryptor.decrypt_all(self._password,
                                                    self._method, data,
                                                    self._crypto_path)
            except Exception:
                logging.debug('UDP handle_server: decrypt data failed')
                return
            if not data:
                logging.debug('UDP handle_server: data is empty after decrypt')
                return
        header_result = parse_header(data)
        if header_result is None:
            return
        addrtype, dest_addr, dest_port, header_length = header_result
        logging.info("udp data to %s:%d from %s:%d" %
                     (dest_addr, dest_port, r_addr[0], r_addr[1]))
        if self._is_local:
            server_addr, server_port = self._get_a_server()
        else:
            server_addr, server_port = dest_addr, dest_port
            # spec https://enuma_elish.org/en/spec/one-time-auth.html
            self._ota_enable_session = addrtype & ADDRTYPE_AUTH
            if self._ota_enable and not self._ota_enable_session:
                logging.warn('client one time auth is required')
                return
            if self._ota_enable_session:
                if len(data) < header_length + ONETIMEAUTH_BYTES:
                    logging.warn('UDP one time auth header is too short')
                    return
                _hash = data[-ONETIMEAUTH_BYTES:]
                data = data[:-ONETIMEAUTH_BYTES]
                _key = iv + key
                if onetimeauth_verify(_hash, data, _key) is False:
                    logging.warn('UDP one time auth fail')
                    return
        addrs = self._dns_cache.get(server_addr, None)
        if addrs is None:
            addrs = socket.getaddrinfo(server_addr, server_port, 0,
                                       socket.SOCK_DGRAM, socket.SOL_UDP)
            if not addrs:
                # drop
                return
            else:
                self._dns_cache[server_addr] = addrs

        af, socktype, proto, canonname, sa = addrs[0]
        key = client_key(r_addr, af)
        client = self._cache.get(key, None)
        if not client:
            # TODO async getaddrinfo
            if self._forbidden_iplist:
                if common.to_str(sa[0]) in self._forbidden_iplist:
                    logging.debug('IP %s is in forbidden list, drop' %
                                  common.to_str(sa[0]))
                    # drop
                    return
            client = socket.socket(af, socktype, proto)
            client.setblocking(False)
            self._cache[key] = client
            self._client_fd_to_server_addr[client.fileno()] = r_addr

            self._sockets.add(client.fileno())
            self._eventloop.add(client, eventloop.POLL_IN, self)

        if self._is_local:
            key, iv, m = cryptor.gen_key_iv(self._password, self._method)
            # spec https://enuma_elish.org/en/spec/one-time-auth.html
            if self._ota_enable_session:
                data = self._ota_chunk_data_gen(key, iv, data)
            try:
                data = cryptor.encrypt_all_m(key, iv, m, self._method, data,
                                             self._crypto_path)
            except Exception:
                logging.debug("UDP handle_server: encrypt data failed")
                return
            if not data:
                return
        else:
            data = data[header_length:]
        if not data:
            return
        try:
            client.sendto(data, (server_addr, server_port))
        except IOError as e:
            err = eventloop.errno_from_exception(e)
            if err in (errno.EINPROGRESS, errno.EAGAIN):
                pass
            else:
                shell.print_exception(e)
Exemplo n.º 9
0
    def _handle_stage_connecting(self, data):
        # logging.info("connecting data: %s "% colored(md5(data).hexdigest(), 'magenta'))
        #pdb.set_trace()

        if not self._is_local:

            if self._ota_enable_session:
                self._ota_chunk_data(data,
                                     self._data_to_write_to_remote.append)
            else:
                if self._tunnel_mode:
                    data = self._cryptor_tunnel.encrypt(data)
                    self._data_to_write_to_remote.append(data)
                    self._update_stream(STREAM_UP, WAIT_STATUS_WRITING)
                    # self._on_remote_write()
                else:
                    self._data_to_write_to_remote.append(data)
            return
        if self._ota_enable_session:
            data = self._ota_chunk_data_gen(data)

        # if self._tunnel_mode:
        #     data = self._cryptor_tunnel.encrypt(data)
        #     logging.info("_handle_stage_connecting e: %s "% data)
        # elif self._is_local:
        data = self._cryptor.encrypt(data)

        self._data_to_write_to_remote.append(data)

        if self._config['fast_open'] and not self._fastopen_connected:
            # for sslocal and fastopen, we basically wait for data and use
            # sendto to connect
            try:
                # only connect once
                self._fastopen_connected = True
                remote_sock = \
                    self._create_remote_socket(self._chosen_server[0],
                                               self._chosen_server[1])
                self._loop.add(remote_sock, eventloop.POLL_ERR, self._server)
                data = b''.join(self._data_to_write_to_remote)
                l = len(data)
                s = remote_sock.sendto(data, MSG_FASTOPEN, self._chosen_server)
                if s < l:
                    data = data[s:]
                    self._data_to_write_to_remote = [data]
                else:
                    self._data_to_write_to_remote = []
                self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING)
            except (OSError, IOError) as e:
                if eventloop.errno_from_exception(e) == errno.EINPROGRESS:
                    # in this case data is not sent at all
                    self._update_stream(STREAM_UP, WAIT_STATUS_READWRITING)
                elif eventloop.errno_from_exception(e) == errno.ENOTCONN:
                    logging.error('fast open not supported on this OS')
                    self._config['fast_open'] = False
                    self.destroy()
                else:
                    shell.print_exception(e)
                    if self._config['verbose']:
                        traceback.print_exc()
                    self.destroy()