Beispiel #1
0
    def visit_simple(self, simple):
        """Visit a Simple keyword."""
        value = simple.value
        self._write(self.decorate_keyword(simple.name))
        self._print_equal(False)
        if isinstance(value, basestring):
            self._write("{0!r}".format(to_str(value)))
        elif isinstance(value, Command):
            self._write(self.decorate_name(value.name))
        elif isinstance(value, CO):
            self._write(self.decorate_special('CO'))
            self._print_left_brace()
            self._write(repr(self.decorate_name(value.name)))
            self._print_right_brace()
        elif isinstance(value, (list, tuple)):
            self._write(obj_start(value))

            for idx, item in enumerate(value):
                if isinstance(item, Command):
                    self._write(self.decorate_name(item.name))
                elif isinstance(item, basestring):
                    self._write("{0!r}".format(to_str(item)))
                else:
                    self._write(repr(item))

                if self._limited(idx, value, self.interrupt_value):
                    break
                self._print_delimiter(idx, value)

            self._write(obj_end(value))
        else:
            self._write("%s" % (value, ))
 def _handle_stage_addr(self, data):
     try:
         if self._is_local:
             cmd = common.ord(data[1])
             if cmd == CMD_UDP_ASSOCIATE:
                 logging.debug('UDP associate')
                 if self._local_sock.family == socket.AF_INET6:
                     header = b'\x05\x00\x00\x04'
                 else:
                     header = b'\x05\x00\x00\x01'
                 addr, port = self._local_sock.getsockname()[:2]
                 addr_to_send = socket.inet_pton(self._local_sock.family,
                                                 addr)
                 port_to_send = struct.pack('>H', port)
                 self._write_to_sock(header + addr_to_send + port_to_send,
                                     self._local_sock)
                 self._stage = STAGE_UDP_ASSOC
                 # just wait for the client to disconnect
                 return
             elif cmd == CMD_CONNECT:
                 # just trim VER CMD RSV
                 data = data[3:]
             else:
                 logging.error('unknown command %d', cmd)
                 self.destroy()
                 return
         header_result = parse_header(data)
         if header_result is None:
             raise Exception('can not parse header')
         addrtype, remote_addr, remote_port, header_length = header_result
         logging.info('connecting %s:%d from %s:%d' %
                      (common.to_str(remote_addr), remote_port,
                       self._client_address[0], self._client_address[1]))
         self._remote_address = (common.to_str(remote_addr), remote_port)
         # pause reading
         self._update_stream(STREAM_UP, WAIT_STATUS_WRITING)
         self._stage = STAGE_DNS
         if self._is_local:
             # forward address to remote
             self._write_to_sock((b'\x05\x00\x00\x01'
                                  b'\x00\x00\x00\x00\x10\x10'),
                                 self._local_sock)
             data_to_send = self._encryptor.encrypt(data)
             self._data_to_write_to_remote.append(data_to_send)
             # notice here may go into _handle_dns_resolved directly
             self._dns_resolver.resolve(self._chosen_server[0],
                                        self._handle_dns_resolved)
         else:
             if len(data) > header_length:
                 self._data_to_write_to_remote.append(data[header_length:])
             # notice here may go into _handle_dns_resolved directly
             self._dns_resolver.resolve(remote_addr,
                                        self._handle_dns_resolved)
     except Exception as e:
         self._log_error(e)
         if self._config['verbose']:
             traceback.print_exc()
         # TODO use logging when debug completed
         self.destroy()
 def _handle_stage_addr(self, data):
     try:
         if self._is_local:
             cmd = common.ord(data[1])
             if cmd == CMD_UDP_ASSOCIATE:
                 logging.debug('UDP associate')
                 if self._local_sock.family == socket.AF_INET6:
                     header = b'\x05\x00\x00\x04'
                 else:
                     header = b'\x05\x00\x00\x01'
                 addr, port = self._local_sock.getsockname()[:2]
                 addr_to_send = socket.inet_pton(self._local_sock.family,
                                                 addr)
                 port_to_send = struct.pack('>H', port)
                 self._write_to_sock(header + addr_to_send + port_to_send,
                                     self._local_sock)
                 self._stage = STAGE_UDP_ASSOC
                 # just wait for the client to disconnect
                 return
             elif cmd == CMD_CONNECT:
                 # just trim VER CMD RSV
                 data = data[3:]
             else:
                 logging.error('unknown command %d', cmd)
                 self.destroy()
                 return
         header_result = parse_header(data)
         if header_result is None:
             raise Exception('can not parse header')
         addrtype, remote_addr, remote_port, header_length = header_result
         logging.info('connecting %s:%d from %s:%d' %
                      (common.to_str(remote_addr), remote_port,
                       self._client_address[0], self._client_address[1]))
         self._remote_address = (common.to_str(remote_addr), remote_port)
         # pause reading
         self._update_stream(STREAM_UP, WAIT_STATUS_WRITING)
         self._stage = STAGE_DNS
         if self._is_local:
             # forward address to remote
             self._write_to_sock((b'\x05\x00\x00\x01'
                                  b'\x00\x00\x00\x00\x10\x10'),
                                 self._local_sock)
             data_to_send = self._encryptor.encrypt(data)
             self._data_to_write_to_remote.append(data_to_send)
             # notice here may go into _handle_dns_resolved directly
             self._dns_resolver.resolve(self._chosen_server[0],
                                        self._handle_dns_resolved)
         else:
             if len(data) > header_length:
                 self._data_to_write_to_remote.append(data[header_length:])
             # notice here may go into _handle_dns_resolved directly
             self._dns_resolver.resolve(remote_addr,
                                        self._handle_dns_resolved)
     except Exception as e:
         self._log_error(e)
         if self._config['verbose']:
             traceback.print_exc()
         # TODO use logging when debug completed
         self.destroy()
 def _create_remote_socket(self, ip, port):
     addrs = socket.getaddrinfo(ip, port, 0, socket.SOCK_STREAM,
                                socket.SOL_TCP)
     if len(addrs) == 0:
         raise Exception("getaddrinfo failed for %s:%d" % (ip, port))
     af, socktype, proto, canonname, sa = addrs[0]
     if self._forbidden_iplist:
         if common.to_str(sa[0]) in self._forbidden_iplist:
             raise Exception('IP %s is in forbidden list, reject' %
                             common.to_str(sa[0]))
     remote_sock = socket.socket(af, socktype, proto)
     self._remote_sock = remote_sock
     self._fd_to_handlers[remote_sock.fileno()] = self
     remote_sock.setblocking(False)
     remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
     return remote_sock
def write_pid_file(pid_file, pid):
    import fcntl
    import stat

    try:
        fd = os.open(pid_file, os.O_RDWR | os.O_CREAT,
                     stat.S_IRUSR | stat.S_IWUSR)
    except OSError as e:
        shell.print_exception(e)
        return -1
    flags = fcntl.fcntl(fd, fcntl.F_GETFD)
    assert flags != -1
    flags |= fcntl.FD_CLOEXEC
    r = fcntl.fcntl(fd, fcntl.F_SETFD, flags)
    assert r != -1
    # There is no platform independent way to implement fcntl(fd, F_SETLK, &fl)
    # via fcntl.fcntl. So use lockf instead
    try:
        fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0, os.SEEK_SET)
    except IOError:
        r = os.read(fd, 32)
        if r:
            logging.error('already started at pid %s' % common.to_str(r))
        else:
            logging.error('already started')
        os.close(fd)
        return -1
    os.ftruncate(fd, 0)
    os.write(fd, common.to_bytes(str(pid)))
    return 0
Beispiel #6
0
 def save_stage(self, stage, text):
     """Save the text representation of a Stage."""
     name = "{0.name}_{0.number}".format(stage)
     filename = name + '.comm'
     self._comm.append(filename)
     with open(osp.join(self.path, filename), 'wb') as fobj:
         fobj.write(to_str(text))
Beispiel #7
0
 def get_host_from_http_header(self, buf):
     ret_buf = b''
     lines = buf.split(b'\r\n')
     if lines and len(lines) > 1:
         for line in lines:
             if match_begin(line, b"Host: "):
                 return common.to_str(line[6:])
Beispiel #8
0
 def _parse_command(self, data):
     # commands:
     # add: {"server_port": 8000, "password": "******"}
     # remove: {"server_port": 8000"}
     data = common.to_str(data)
     parts = data.split(':', 1)
     if len(parts) < 2:
         return data, None
     command, config_json = parts
     try:
         config = shell.parse_json_in_str(config_json)
         if 'method' in config:
             config['method'] = common.to_str(config['method'])
         return command, config
     except Exception as e:
         logging.error(e)
         return None
Beispiel #9
0
 def __init__(self, method):
     method = common.to_str(method)
     self.method = method
     self._method_info = self.get_method_info(method)
     if self._method_info:
         self.obfs = self.get_obfs(method)
     else:
         raise Exception('obfs plugin [%s] not supported' % method)
Beispiel #10
0
    def shortrepr(self):
        """
        Get a short representation of the node.

        Returns:
            str: Short node's representation.
        """
        return to_str("{0._name} <{0._id}:{0.__class__.__name__}"
                      ">").format(self)
 def load(self, data_path, session, ignore_missing=True):
     '''
     Load network weights.
     data_path: The path to the numpy-serialized network weights
     session: The current TensorFlow session
     ignore_missing: If true, serialized weights for missing layers are ignored.
     '''
     data_dict = np.load(data_path, encoding='bytes',
                         allow_pickle=True).item()
     print(data_dict.keys())
     for op_name, param_dict in data_dict.items():
         if isinstance(data_dict[op_name], np.ndarray):
             if 'RMSProp' in op_name:
                 continue
             with tf.variable_scope('', reuse=True):
                 var = tf.get_variable(op_name.replace(':0', ''))
                 try:
                     session.run(var.assign(data_dict[op_name]))
                 except Exception as e:
                     print(op_name)
                     print(e)
                     sys.exit(-1)
         else:
             op_name = common.to_str(op_name)
             # if op_name > 'conv4':
             #     print(op_name, 'skipped')
             #     continue
             # print(op_name, 'restored')
             with tf.variable_scope(op_name, reuse=True):
                 for param_name, data in param_dict.items():
                     try:
                         var = tf.get_variable(common.to_str(param_name))
                         session.run(var.assign(data))
                     except ValueError as e:
                         print(e)
                         if not ignore_missing:
                             raise
def check_config(config, is_local):
    if config.get('daemon', None) == 'stop':
        # no need to specify configuration for daemon stop
        return

    if is_local and not config.get('password', None):
        logging.error('password not specified')
        print_help(is_local)
        sys.exit(2)

    if not is_local and not config.get('password', None) \
            and not config.get('port_password', None):
        logging.error('password or port_password not specified')
        print_help(is_local)
        sys.exit(2)

    if 'local_port' in config:
        config['local_port'] = int(config['local_port'])

    if 'server_port' in config and type(config['server_port']) != list:
        config['server_port'] = int(config['server_port'])

    if config.get('local_address', '') in [b'0.0.0.0']:
        logging.warn('warning: local set to listen on 0.0.0.0, it\'s not safe')
    if config.get('server', '') in ['127.0.0.1', 'localhost']:
        logging.warn('warning: server set to listen on %s:%s, are you sure?' %
                     (to_str(config['server']), config['server_port']))
    if (config.get('method', '') or '').lower() == 'table':
        logging.warn('warning: table is not safe; please use a safer cipher, '
                     'like AES-256-CFB')
    if (config.get('method', '') or '').lower() == 'rc4':
        logging.warn('warning: RC4 is not safe; please use a safer cipher, '
                     'like AES-256-CFB')
    if config.get('timeout', 300) < 100:
        logging.warn('warning: your timeout %d seems too short' %
                     int(config.get('timeout')))
    if config.get('timeout', 300) > 600:
        logging.warn('warning: your timeout %d seems too long' %
                     int(config.get('timeout')))
    if config.get('password') in [b'mypassword']:
        logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your '
                      'config.json!')
        sys.exit(1)
    if config.get('user', None) is not None:
        if os.name != 'posix':
            logging.error('user can be used only on Unix')
            sys.exit(1)

    encrypt.try_cipher(config['password'], config['method'])
Beispiel #13
0
    def export(self, file_name):
        """
        Export Stage to a COMM file.

        Arguments:
            file_name (str): Path to the COMM file.
        """
        from common import to_unicode
        utext = to_unicode(self.get_text(enclosed=True))
        if not utext.endswith('\n'):
            utext += '\n'

        with open(file_name, 'w') as handle:
            handle.write(to_str(utext))
            handle.flush()
Beispiel #14
0
    def __str__(self):
        """Stringifies a command: returns is code_aster syntax"""
        ostream = StringIO()

        from ..study2comm import ExportToCommVisitor
        export = ExportToCommVisitor(ostream)

        comment = self.comment  # Print corresponding comment first
        if comment is not None:
            comment.accept(export)  # pragma pylint: disable=no-member

        self.accept(export)

        value = ostream.getvalue()

        return to_str(value[1:])
Beispiel #15
0
 def _local_read(self):
     sock = self._local_sock
     try:
         data = sock.recv(BUF_SIZE)
     except (OSError, IOError) as e:
         logging.exception(e)
         return
     if not data:
         self.destroy()
         return
     logging.info("recv local read len: %s", len(data))
     if data == b'\x05\x01\x00':
         self.write_to_sock(b'\x05\00', self._local_sock)
         return
     if b'\x05\x01\x00' == data[:3]:
         data = data[3:]
     if self._stage == STAGE_INIT:
         self.req_data += data
         logging.info("POLL_IN event fd:%s", sock.fileno())
         header_result = parse_header(self.req_data)
         if not header_result:
             logging.error("[parse_header] error res:%s data:%s",
                           header_result, self.req_data)
             return
         self._stage = STAGE_STREAM
         addrtype, remote_addr, remote_port, header_length = header_result
         logging.info('connecting %s:%d from %s:%d header_len:%s' %
                      (common.to_str(remote_addr), remote_port,
                       self._client_address[0], self._client_address[1],
                       header_length))
         self.req_data = self.req_data[header_length:]
         remote_sock = create_remote_socket(remote_addr, remote_port)
         self._remote_sock = remote_sock
         self.write_to_sock(b'\x05\x00\x00\x01\x00\x00\x00\x00\x10\x10',
                            self._local_sock)
         logging.info("connecting %s:%s from :%s", remote_addr, remote_port,
                      self._client_address)
         logging.info("remote_sock:%s local_sock:%s",
                      self._remote_sock.fileno(), self._local_sock.fileno())
         self.loop.add(remote_sock, POLL_OUT | POLL_ERR, self)
     elif self._stage == STAGE_STREAM:
         data = self.req_data + data
         self.req_data = bytes()
         self.write_to_sock(data, self._remote_sock)
def daemon_stop(pid_file):
    import errno
    try:
        with open(pid_file) as f:
            buf = f.read()
            pid = common.to_str(buf)
            if not buf:
                logging.error('not running')
    except IOError as e:
        shell.print_exception(e)
        if e.errno == errno.ENOENT:
            # always exit 0 if we are sure daemon is not running
            logging.error('not running')
            return
        sys.exit(1)
    pid = int(pid)
    if pid > 0:
        try:
            os.kill(pid, signal.SIGTERM)
        except OSError as e:
            if e.errno == errno.ESRCH:
                logging.error('not running')
                # always exit 0 if we are sure daemon is not running
                return
            shell.print_exception(e)
            sys.exit(1)
    else:
        logging.error('pid is not positive: %d', pid)

    # sleep for maximum 10s
    for i in range(0, 200):
        try:
            # query for the pid
            os.kill(pid, 0)
        except OSError as e:
            if e.errno == errno.ESRCH:
                break
        time.sleep(0.05)
    else:
        logging.error('timed out when stopping pid %d', pid)
        sys.exit(1)
    print('stopped')
    os.unlink(pid_file)
Beispiel #17
0
def main():

    shell.check_python()

    # fix py2exe
    if hasattr(sys, "frozen") and sys.frozen in  ("windows_exe", "console_exe"):
        p = os.path.dirname(os.path.abspath(sys.executable))
        os.chdir(p)

    config = shell.get_config(True)

    #added by tib for local random choose a server and the port and the port_password
    if config['port_password']:
        if config['password']:
            logging.warn('warning: port_password should not be used with server_port and password. server_port and password will be ignored')
#         config['server_port'] = int(random.choice(config['port_password'].items())[0])        
        if config.has_key('server_port'):
            if type(config['server_port']) == list and config['server_port']:
                config['server_port'] = random.choice(config.get('server_port', 8388))
            elif config['server_port']:
                config['server_port'] == int(common.to_str(config.get('server_port',8388)))
            else:
                config['server_port'] = int(random.choice(config['port_password'].items())[0])
        else:
            config['server_port'] = int(random.choice(config['port_password'].items())[0])
        config['password'] = common.to_str(config['port_password']["%s" % config['server_port']])
    else:
        if type(config['server_port']) == list and config['server_port']:
            config['server_port'] = random.choice(config.get('server_port', 8388))
        else:
            config['server_port'] == int(common.to_str(config.get('server_port',8388)))
        config["password"] = str(config["port_password"]["%s" % config["server_port"]]).strip()

    logging.warn('!' * 30)
    logging.info("OK.. I choose this guy to help me f**k the GFW.. [ %s : %s : %s : %s : %s]" % (config['server'],config['server_port'],config['password'],config['server_info']["%s" % config['server']],config['method']))
    logging.warn('!' * 30)
    time.sleep(1)

    daemon.daemon_exec(config)

    try:
        logging.info("starting local at %s:%d" % (config['local_address'], config['local_port']))

        dns_resolver = asyncdns.DNSResolver(config)
        tcp_server = tcprelay.TCPRelay(config, dns_resolver, True)
        udp_server = udprelay.UDPRelay(config, dns_resolver, True)
        loop = eventloop.EventLoop(config)
        dns_resolver.add_to_loop(loop)
        tcp_server.add_to_loop(loop)
        udp_server.add_to_loop(loop)

        def handler(signum, _):
            logging.warn('received SIGQUIT, doing graceful shutting down..')
            tcp_server.close(next_tick=True)
            udp_server.close(next_tick=True)
        signal.signal(getattr(signal, 'SIGQUIT', signal.SIGTERM), handler)

        def int_handler(signum, _):
            sys.exit(1)
        signal.signal(signal.SIGINT, int_handler)

        daemon.set_user(config.get('user', None))
        loop.run()
    except Exception as e:
        shell.print_exception(e)
        sys.exit(1)
def get_config(is_local):
    global verbose
    global config

    if is_local:
        shortopts = 'hd:s:b:p:k:l:m:c:t:vq'
        longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'user='******'version']
    else:
        shortopts = 'hd:s:p:k:m:c:t:vq'
        longopts = ['help', 'fast-open', 'pid-file=', 'log-file=', 'workers=',
                    'forbidden-ip=', 'user='******'version']
    try:
        config_path = find_config()
        optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
        for key, value in optlist:
            if key == '-c':
                config_path = value

        if config_path:
            logging.info('loading config from %s' % config_path)
            with open(config_path, 'rb') as f:
                try:
                    config = json.loads(f.read().decode('utf8'),
                                        object_hook=_decode_dict)
                except ValueError as e:
                    logging.error('found an error in config.json: %s',
                                  e.message)
                    sys.exit(1)
        else:
            config = {}

        optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
        v_count = 0
        for key, value in optlist:
            if key == '-p':
                config['server_port'] = int(value)
            elif key == '-k':
                config['password'] = to_bytes(value)
            elif key == '-l':
                config['local_port'] = int(value)
            elif key == '-s':
                config['server'] = to_str(value)
            elif key == '-m':
                config['method'] = to_str(value)
            elif key == '-b':
                config['local_address'] = to_str(value)
            elif key == '-v':
                v_count += 1
                # '-vv' turns on more verbose mode
                config['verbose'] = v_count
            elif key == '-t':
                config['timeout'] = int(value)
            elif key == '--fast-open':
                config['fast_open'] = True
            elif key == '--workers':
                config['workers'] = int(value)
            elif key == '--user':
                config['user'] = to_str(value)
            elif key == '--forbidden-ip':
                config['forbidden_ip'] = to_str(value).split(',')
            elif key in ('-h', '--help'):
                if is_local:
                    print_local_help()
                else:
                    print_server_help()
                sys.exit(0)
            elif key == '--version':
                print_shadowsocks()
                sys.exit(0)
            elif key == '-d':
                config['daemon'] = to_str(value)
            elif key == '--pid-file':
                config['pid-file'] = to_str(value)
            elif key == '--log-file':
                config['log-file'] = to_str(value)
            elif key == '-q':
                v_count -= 1
                config['verbose'] = v_count
    except getopt.GetoptError as e:
        print(e, file=sys.stderr)
        print_help(is_local)
        sys.exit(2)

    if not config:
        logging.error('config not specified')
        print_help(is_local)
        sys.exit(2)

    config['password'] = to_bytes(config.get('password', b''))
    config['method'] = to_str(config.get('method', 'aes-256-cfb'))
    config['port_password'] = config.get('port_password', None)
    config['timeout'] = int(config.get('timeout', 300))
    config['fast_open'] = config.get('fast_open', False)
    config['workers'] = config.get('workers', 1)
    config['pid-file'] = config.get('pid-file', '/var/run/shadowsocks.pid')
    config['log-file'] = config.get('log-file', '/var/log/shadowsocks.log')
    config['workers'] = config.get('workers', 1)
    config['verbose'] = config.get('verbose', False)
    config['local_address'] = to_str(config.get('local_address', '127.0.0.1'))
    config['local_port'] = config.get('local_port', 1080)
    if is_local:
        if config.get('server', None) is None:
            logging.error('server addr not specified')
            print_local_help()
            sys.exit(2)
        else:
            config['server'] = to_str(config['server'])
    else:
        config['server'] = to_str(config.get('server', '0.0.0.0'))
        try:
            config['forbidden_ip'] = \
                IPNetwork(config.get('forbidden_ip', '127.0.0.0/8,::1/128'))
        except Exception as e:
            logging.error(e)
            sys.exit(2)
    config['server_port'] = config.get('server_port', 8388)

    logging.getLogger('').handlers = []
    logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE')
    if config['verbose'] >= 2:
        level = VERBOSE_LEVEL
    elif config['verbose'] == 1:
        level = logging.DEBUG
    elif config['verbose'] == -1:
        level = logging.WARN
    elif config['verbose'] <= -2:
        level = logging.ERROR
    else:
        level = logging.INFO
    verbose = config['verbose']
    logging.basicConfig(level=level,
                        format='%(asctime)s %(levelname)-8s %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        filemode='w')
    # 定义一个1分钟换一次log文件的handler
    filehandler = logging.handlers.TimedRotatingFileHandler(
        "/var/log/shadowsocks/sslog", 'D', 2, 15)
    filehandler.suffix = "%Y%m%d-%H%M.log"
    # set a format which is simpler for console use
    formatter = logging.Formatter("%(asctime)s %(levelname)-8s %(message)s","%Y-%m-%d %H:%M:%S");
    filehandler.setFormatter(formatter)
    #logging.getLogger('').addHandler(console)
    logging.getLogger('').addHandler(filehandler)

    check_config(config, is_local)

    return config
    def _handle_server(self):
        server = self._server_socket
        data, r_addr = server.recvfrom(BUF_SIZE)
        if not data:
            logging.debug('UDP handle_server: data is empty')
        if self._is_local:
            frag = common.ord(data[2])
            if frag != 0:
                logging.warn('drop a message since frag is not 0')
                return
            else:
                data = data[3:]
        else:
            data = encrypt.encrypt_all(self._password, self._method, 0, data)
            # decrypt data
            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

        if self._is_local:
            server_addr, server_port = self._get_a_server()
        else:
            server_addr, server_port = dest_addr, dest_port

        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)
        logging.debug(key)
        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)

        if self._is_local:
            data = encrypt.encrypt_all(self._password, self._method, 1, data)
            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)