Ejemplo n.º 1
0
Archivo: ss.py Proyecto: rain10154/ss
def getSimpleConfig():
    VERBOSE_LEVEL = 5
    config['password'] = to_bytes(config.get('password', b''))
    config['method'] = to_str(config.get('method', 'aes-256-cfb'))
    config['port_password'] = ssDict
    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['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)
    config['server'] = to_str(config.get('server', '0.0.0.0'))

    logging.getLogger('').handlers = []
    logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE')
    level = logging.INFO
    logging.basicConfig(level=level,
                        format='%(asctime)s %(levelname)-8s %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')

    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)

    check_config(config, False)
    return config
Ejemplo n.º 2
0
    def _create_remote_socket(self, ip, port):
        if self._remote_udp:
            addrs_v6 = socket.getaddrinfo("::", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
            addrs = socket.getaddrinfo("0.0.0.0", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
        else:
            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

        if self._remote_udp:
            af, socktype, proto, canonname, sa = addrs_v6[0]
            remote_sock_v6 = socket.socket(af, socktype, proto)
            self._remote_sock_v6 = remote_sock_v6
            self._fd_to_handlers[remote_sock_v6.fileno()] = self
            remote_sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 32)
            remote_sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1024 * 32)
            remote_sock_v6.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 32)
            remote_sock_v6.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1024 * 32)

        remote_sock.setblocking(False)
        if self._remote_udp:
            pass
        else:
            remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
        return remote_sock
Ejemplo n.º 3
0
 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()
Ejemplo n.º 4
0
    def _create_remote_socket(self, ip, port):
        if self._remote_udp:
            addrs_v6 = socket.getaddrinfo("::", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
            addrs = socket.getaddrinfo("0.0.0.0", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
        else:
            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 not self._remote_udp:
            if self._forbidden_iplist:
                if common.to_str(sa[0]) in self._forbidden_iplist:
                    if self._remote_address:
                        raise Exception('IP %s is in forbidden list, when connect to %s:%d via port %d' %
                            (common.to_str(sa[0]), self._remote_address[0], self._remote_address[1], self._server._listen_port))
                    raise Exception('IP %s is in forbidden list, reject' %
                                    common.to_str(sa[0]))
            if self._forbidden_portset:
                if sa[1] in self._forbidden_portset:
                    if self._remote_address:
                        raise Exception('Port %d is in forbidden list, when connect to %s:%d via port %d' %
                            (sa[1], self._remote_address[0], self._remote_address[1], self._server._listen_port))
                    raise Exception('Port %d is in forbidden list, reject' % sa[1])
        remote_sock = socket.socket(af, socktype, proto)
        self._remote_sock = remote_sock
        self._fd_to_handlers[remote_sock.fileno()] = self

        if self._remote_udp:
            af, socktype, proto, canonname, sa = addrs_v6[0]
            remote_sock_v6 = socket.socket(af, socktype, proto)
            self._remote_sock_v6 = remote_sock_v6
            self._fd_to_handlers[remote_sock_v6.fileno()] = self

        remote_sock.setblocking(False)
        if self._remote_udp:
            remote_sock_v6.setblocking(False)
        else:
            remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)

            if not self._is_local:
                bind_addr = ''
                if self._bind and af == socket.AF_INET:
                    bind_addr = self._bind
                elif self._bindv6 and af == socket.AF_INET6:
                    bind_addr = self._bindv6
                else:
                    bind_addr = self._accept_address[0]

                bind_addr = bind_addr.replace("::ffff:", "")
                if bind_addr in self._ignore_bind_list:
                    bind_addr = None
                if bind_addr:
                    local_addrs = socket.getaddrinfo(bind_addr, port, 0, socket.SOCK_STREAM, socket.SOL_TCP)
                    if local_addrs[0][0] == af:
                        logging.debug("bind %s" % (bind_addr,))
                        remote_sock.bind((bind_addr, 0))
        return remote_sock
Ejemplo n.º 5
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:])
Ejemplo n.º 6
0
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:
        logging.error(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
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
	def ssrlink(self, user, encode):
		protocol = user.get('protocol', '')
		obfs = user.get('obfs', '')
		protocol = protocol.replace("_compatible", "")
		obfs = obfs.replace("_compatible", "")
		link = "%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))
		return "ssr://" + ( encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
Ejemplo n.º 9
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
Ejemplo n.º 10
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)
Ejemplo n.º 11
0
	def ssrlink(self, user, encode, muid):
		protocol = user.get('protocol', '')
		obfs = user.get('obfs', '')
		protocol = protocol.replace("_compatible", "")
		obfs = obfs.replace("_compatible", "")
		protocol_param = ''
		if muid is not None:
			protocol_param_ = user.get('protocol_param', '')
			param = protocol_param_.split('#')
			if len(param) == 2:
				for row in self.data.json:
					if int(row['port']) == muid:
						param = str(muid) + ':' + row['passwd']
						protocol_param = '/?protoparam=' + common.to_str(base64.urlsafe_b64encode(common.to_bytes(param))).replace("=", "")
						break
		link = ("%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", ""))) + protocol_param
		return "ssr://" + (encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link)
Ejemplo n.º 12
0
def write_pid_file(pid_file, pid):
    """
    pidfile

    通常在 /var/run 目录中会看到很多进程的 pid 文件, 其实这些文件就是一个记录着进程的 PID 号的文本文件。
    它的作用是防止程序启动多个副本,只有获得 pid 文件写入权限的进程才能正常启动并把进程 PID 写入到该文件,
    而同一程序的其他进程则会检测到该文件无法写入退出。

    """
    # 文件描述符控制
    import fcntl
    # 获取文件信息
    import stat

    try:
        # O_RDWR | O_CREAT 如果文件存在,打开文件以读取写入,否则创建该文件,并使其拥有以下权限
        # S_IRUSR 文件所有者具可读取权限
        # S_IWUSR 文件所有者具可写入权限
        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
    # F_GETFD 获取文件描述符标记
    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:
        """
        文件锁
        LOCK_EX exclusive 独占锁
        LOCK_NB non-blocking 非阻塞锁
        在独占锁的情况下,同一时间只有一个进程可以锁住这个文件。
        在有其他进程占有该锁时,
        如果是阻塞锁,lockf 函数会一直阻塞,直到获得锁,而非阻塞锁使 lockf 函数直接返回 IOError。
        fcntl.lockf(fd, operation[, length[, start[, whence]]])
        start 和 length 标记了要锁住的区域的起始位置和长度,而 whence 标记了整个锁区域的偏移量。
        SEEK_SET SEEK_CUR SEEK_END 分别表示文件开头,当前指针位置和文件结尾
        """
        fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0, os.SEEK_SET)
    except IOError:
        # pidfile 被其他进程锁住的情况,读取该 pidfile 内容
        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
    # 把 fd 对应文件修剪为长度为 0,即清空该文件
    os.ftruncate(fd, 0)
    # 将当前进程的 pid 文件写入到 fd 对应文件中
    os.write(fd, common.to_bytes(str(pid)))
    return 0
Ejemplo n.º 13
0
    def _create_remote_socket(self, ip, port):
        if self._remote_udp:
            addrs_v6 = socket.getaddrinfo("::", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
            addrs = socket.getaddrinfo("0.0.0.0", 0, 0, socket.SOCK_DGRAM, socket.SOL_UDP)
        else:
            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 not self._remote_udp:
            if self._forbidden_iplist:
                if common.to_str(sa[0]) in self._forbidden_iplist:
                    if self._remote_address:
                        raise Exception('IP %s is in forbidden list, when connect to %s:%d via port %d' %
                            (common.to_str(sa[0]), self._remote_address[0], self._remote_address[1], self._server._listen_port))
                    raise Exception('IP %s is in forbidden list, reject' %
                                    common.to_str(sa[0]))
            if self._forbidden_portset:
                if sa[1] in self._forbidden_portset:
                    if self._remote_address:
                        raise Exception('Port %d is in forbidden list, when connect to %s:%d via port %d' %
                            (sa[1], self._remote_address[0], self._remote_address[1], self._server._listen_port))
                    raise Exception('Port %d is in forbidden list, reject' % sa[1])
        remote_sock = socket.socket(af, socktype, proto)
        self._remote_sock = remote_sock
        self._fd_to_handlers[remote_sock.fileno()] = self

        if self._remote_udp:
            af, socktype, proto, canonname, sa = addrs_v6[0]
            remote_sock_v6 = socket.socket(af, socktype, proto)
            self._remote_sock_v6 = remote_sock_v6
            self._fd_to_handlers[remote_sock_v6.fileno()] = self

        remote_sock.setblocking(False)
        if self._remote_udp:
            remote_sock_v6.setblocking(False)

            if not self._is_local:
                self._socket_bind_addr(remote_sock, af)
                self._socket_bind_addr(remote_sock_v6, af)
        else:
            remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
            if not self._is_local:
                self._socket_bind_addr(remote_sock, af)
        return remote_sock
Ejemplo n.º 14
0
 def push(self, ch):
     ch = ord(ch)
     if self.state == 0:
         if ch == ord('"'):
             self.state = 1
             return to_str(chr(ch))
         elif ch == ord('/'):
             self.state = 3
         else:
             return to_str(chr(ch))
     elif self.state == 1:
         if ch == ord('"'):
             self.state = 0
             return to_str(chr(ch))
         elif ch == ord('\\'):
             self.state = 2
         return to_str(chr(ch))
     elif self.state == 2:
         self.state = 1
         if ch == ord('"'):
             return to_str(chr(ch))
         return "\\" + to_str(chr(ch))
     elif self.state == 3:
         if ch == ord('/'):
             self.state = 4
         else:
             return "/" + to_str(chr(ch))
     elif self.state == 4:
         if ch == ord('\n'):
             self.state = 0
             return "\n"
     return ""
Ejemplo n.º 15
0
 def push(self, ch):
     ch = ord(ch)
     if self.state == 0:
         if ch == ord('"'):
             self.state = 1
             return to_str(chr(ch))
         elif ch == ord('/'):
             self.state = 3
         else:
             return to_str(chr(ch))
     elif self.state == 1:
         if ch == ord('"'):
             self.state = 0
             return to_str(chr(ch))
         elif ch == ord('\\'):
             self.state = 2
         return to_str(chr(ch))
     elif self.state == 2:
         self.state = 1
         if ch == ord('"'):
             return to_str(chr(ch))
         return "\\" + to_str(chr(ch))
     elif self.state == 3:
         if ch == ord('/'):
             self.state = 4
         else:
             return "/" + to_str(chr(ch))
     elif self.state == 4:
         if ch == ord('\n'):
             self.state = 0
             return "\n"
     return ""
Ejemplo n.º 16
0
 def _create_remote_socket(self, ip, port):
     # getaddrinfo() resolves host and port into addrinfo struct.
     # it returns list of (family, socktype, proto, canonname, sockaddr)
     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
Ejemplo n.º 17
0
def check_config(config, is_local):
    """
    校验配置文件正确性
    :param config:
    :param is_local: 是否为本地启动(也就是客户端)
    :return:
    """
    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) \
            and not config.get('manager_address'):
        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 config.get('server_port', None) 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'])
Ejemplo n.º 18
0
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) \
            and not config.get('manager_address'):
        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 config.get('server_port', None) 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 'cc_algo' in config and not sys.platform.startswith('linux'):
        logging.warn('pluggable congestion control algorithm requires linux!')
    if config.get('user', None) is not None:
        if os.name != 'posix':
            logging.error('user can be used only on Unix')
            sys.exit(1)
    if 'cc_algo' in config and sys.platform.startswith('linux'):
        logging.info('use %s as socket congestion control algorithm' %
                     config.get('cc_algo', None))
    encrypt.try_cipher(config['password'], config['method'])
Ejemplo n.º 19
0
 def _create_remote_socket(self, ip, port):
     logging.info("IP:PORT %s:%s", ip, port)
     if port == 80:
         addrs = socket.getaddrinfo('127.0.0.1', '8899', 0, socket.SOCK_STREAM,
                                socket.SOL_TCP)
     else:
         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
Ejemplo n.º 20
0
def daemon_stop(pid_file):
    """ Kill the daemon process if it's running.

    After calling kill to stop the process,
    wait at most 10 seconds and check if the process is stopped finally.
    :param pid_file: The file from which we can get the process id.
    """
    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:
        logging.error(e)

        # No such pid_file, which means the daemon is not running.
        if e.errno == errno.ENOENT:
            logging.error('not running')
            return
        sys.exit(1)
    pid = int(pid)
    if pid > 0:
        try:
            os.kill(pid, signal.SIGTERM)
        except OSError as e:
            # No process with pid is running now, so just return 0.
            if e.errno == errno.ESRCH:
                logging.error('not running')
                return
            logging.error(e)
            sys.exit(1)
    else:
        logging.error('pid is not positive: %d', pid)

    # Sleep for maximum 10s to wait for the process stopped.
    for i in range(0, 200):
        try:
            # Check whether the process with pid is running or not.
            # Ref: https://github.com/xuelangZF/AnnotatedShadowSocks/issues/30
            os.kill(pid, 0)
        except OSError as e:
            if e.errno == errno.ESRCH:
                break
        time.sleep(0.05)

    # Ref: https://github.com/xuelangZF/AnnotatedShadowSocks/issues/29
    else:
        logging.error('timed out when stopping pid %d', pid)
        sys.exit(1)
    print('stopped')
    os.unlink(pid_file)
Ejemplo n.º 21
0
    def _create_remote_socket(self, ip, port):
        if self._remote_udp:
            addrs_v6 = socket.getaddrinfo("::", 0, 0, socket.SOCK_DGRAM,
                                          socket.SOL_UDP)
            addrs = socket.getaddrinfo("0.0.0.0", 0, 0, socket.SOCK_DGRAM,
                                       socket.SOL_UDP)
        else:
            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

        if self._remote_udp:
            af, socktype, proto, canonname, sa = addrs_v6[0]
            remote_sock_v6 = socket.socket(af, socktype, proto)
            self._remote_sock_v6 = remote_sock_v6
            self._fd_to_handlers[remote_sock_v6.fileno()] = self
            remote_sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF,
                                   1024 * 32)
            remote_sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF,
                                   1024 * 32)
            remote_sock_v6.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF,
                                      1024 * 32)
            remote_sock_v6.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF,
                                      1024 * 32)

        remote_sock.setblocking(False)
        if self._remote_udp:
            pass
        else:
            remote_sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
        return remote_sock
Ejemplo n.º 22
0
	def new_server(self, port, user_config):
		ret = True
		port = int(port)
		ipv6_ok = False

		if 'server_ipv6' in self.config:
			if port in self.tcp_ipv6_servers_pool:
				logging.info("server already at %s:%d" % (self.config['server_ipv6'], port))
				return 'this port server is already running'
			else:
				a_config = self.config.copy()
				a_config.update(user_config)
				if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]":
					a_config['server_ipv6'] = a_config['server_ipv6'][1:-1]
				a_config['server'] = a_config['server_ipv6']
				a_config['server_port'] = port
				a_config['max_connect'] = 128
				a_config['method'] = common.to_str(a_config['method'])
				try:
					logging.info("starting server at [%s]:%d" % (common.to_str(a_config['server']), port))

					tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
					tcp_server.add_to_loop(self.loop)
					self.tcp_ipv6_servers_pool.update({port: tcp_server})

					udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
					udp_server.add_to_loop(self.loop)
					self.udp_ipv6_servers_pool.update({port: udp_server})

					if common.to_str(a_config['server_ipv6']) == "::":
						ipv6_ok = True
				except Exception as e:
					logging.warn("IPV6 %s " % (e,))

		if 'server' in self.config:
			if port in self.tcp_servers_pool:
				logging.info("server already at %s:%d" % (common.to_str(self.config['server']), port))
				return 'this port server is already running'
			else:
				a_config = self.config.copy()
				a_config.update(user_config)
				a_config['server_port'] = port
				a_config['max_connect'] = 128
				a_config['method'] = common.to_str(a_config['method'])
				try:
					logging.info("starting server at %s:%d" % (common.to_str(a_config['server']), port))

					tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False)
					tcp_server.add_to_loop(self.loop)
					self.tcp_servers_pool.update({port: tcp_server})

					udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False)
					udp_server.add_to_loop(self.loop)
					self.udp_servers_pool.update({port: udp_server})

				except Exception as e:
					if not ipv6_ok:
						logging.warn("IPV4 %s " % (e,))

		return True
Ejemplo n.º 23
0
	def new_server(self, port, user_config):
		ret = True
		port = int(port)
		ipv6_ok = False

		if 'server_ipv6' in self.config:
			if port in self.tcp_ipv6_servers_pool:
				logging.info("server already at %s:%d" % (self.config['server_ipv6'], port))
				return 'this port server is already running'
			else:
				a_config = self.config.copy()
				a_config.update(user_config)
				if len(a_config['server_ipv6']) > 2 and a_config['server_ipv6'][0] == "[" and a_config['server_ipv6'][-1] == "]":
					a_config['server_ipv6'] = a_config['server_ipv6'][1:-1]
				a_config['server'] = a_config['server_ipv6']
				a_config['server_port'] = port
				a_config['max_connect'] = 128
				a_config['method'] = common.to_str(a_config['method'])
				try:
					logging.info("starting server at [%s]:%d" % (common.to_str(a_config['server']), port))

					tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
					tcp_server.add_to_loop(self.loop)
					self.tcp_ipv6_servers_pool.update({port: tcp_server})

					udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False, stat_counter=self.stat_counter)
					udp_server.add_to_loop(self.loop)
					self.udp_ipv6_servers_pool.update({port: udp_server})

					if common.to_str(a_config['server_ipv6']) == "::":
						ipv6_ok = True
				except Exception as e:
					logging.warn("IPV6 %s " % (e,))

		if 'server' in self.config:
			if port in self.tcp_servers_pool:
				logging.info("server already at %s:%d" % (common.to_str(self.config['server']), port))
				return 'this port server is already running'
			else:
				a_config = self.config.copy()
				a_config.update(user_config)
				a_config['server_port'] = port
				a_config['max_connect'] = 128
				a_config['method'] = common.to_str(a_config['method'])
				try:
					logging.info("starting server at %s:%d" % (common.to_str(a_config['server']), port))

					tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False)
					tcp_server.add_to_loop(self.loop)
					self.tcp_servers_pool.update({port: tcp_server})

					udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False)
					udp_server.add_to_loop(self.loop)
					self.udp_servers_pool.update({port: udp_server})

				except Exception as e:
					if not ipv6_ok:
						logging.warn("IPV4 %s " % (e,))

		return True
Ejemplo n.º 24
0
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)
        and not config.get("manager_address")
    ):
        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 config.get("server_port", None) 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 "cc_algo" in config and not sys.platform.startswith("linux"):
        logging.warn("pluggable congestion control algorithm requires linux!")
    if config.get("user", None) is not None:
        if os.name != "posix":
            logging.error("user can be used only on Unix")
            sys.exit(1)
    if "cc_algo" in config and sys.platform.startswith("linux"):
        logging.info("use %s as socket congestion control algorithm" % config.get("cc_algo", None))
    encrypt.try_cipher(config["password"], config["method"])
Ejemplo n.º 25
0
 def _create_remote_socket(self, ip, port):
     """
     创建远程socket
     :param ip:      待连接的ip地址
     :param port:    待连接的端口地址
     :return:        remote_socket
     """
     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
Ejemplo n.º 26
0
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) and not config.get('port_password',None):
        logging.error('password or port_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['log'].has_key('log_enable') is False:
        config['log'] = {}
        config['log']['log_enable'] = False
    if config['log'].has_key('log_path') is False:
        config['log']['log_path'] = "%s" % os.path.expanduser("~/ss.log")
    if config['forbid'].has_key('site') is False:
        config['forbid'] = {}
        config['forbid']['site'] = []
    if config['forbid'].has_key('port') is False:
        config['forbid']['port'] = []
    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'])
Ejemplo n.º 27
0
def daemon_exec(config):
    if 'daemon' in config:
        if os.name != 'posix':
            raise Exception('daemon mode is only supported on Unix')
        command = config['daemon']
        if not command:
            command = 'start'
        pid_file = config['pid-file']
        log_file = config['log-file']
        command = common.to_str(command)
        pid_file = common.to_str(pid_file)
        log_file = common.to_str(log_file)
        if command == 'start':
            daemon_start(pid_file, log_file)
        elif command == 'stop':
            daemon_stop(pid_file)
            # always exit after daemon_stop
            sys.exit(0)
        elif command == 'restart':
            daemon_stop(pid_file)
            daemon_start(pid_file, log_file)
        else:
            raise Exception('unsupported daemon command %s' % command)
Ejemplo n.º 28
0
 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)
     if not self._is_local:
         bind_addr = self._local_sock.getsockname()[0]
         local_addrs = socket.getaddrinfo(bind_addr, port, 0,
                                          socket.SOCK_STREAM,
                                          socket.SOL_TCP)
         if local_addrs[0][0] == af:
             remote_sock.bind((bind_addr, 0))
     return remote_sock
Ejemplo n.º 29
0
def daemon_stop(pid_file):
    """
    终止守护线程

    :param pid_file: 保存守护线程pid的文件

    :return: None
    """
    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)
Ejemplo n.º 30
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)
         return command, config
     except Exception as e:
         logging.error(e)
         return None
Ejemplo n.º 31
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 = json.loads(config_json)
         return command, config
     except Exception as e:
         logging.error(e)
         return None
Ejemplo n.º 32
0
    def _handle_stage_addr(self, data):
        data = data[11:]
        try:
            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 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()
            self.destroy()
Ejemplo n.º 33
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)
         return command, config
     except Exception as e:
         logging.error(e)
         return None
Ejemplo n.º 34
0
def daemon_stop(pid_file):
    import errno
    try:
        with open(pid_file) as f:
            # 读出pid数值
            buf = f.read()
            pid = common.to_str(buf)
            if not buf:
                logging.error('not running')
    except IOError as e:
        logging.error(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:
            # 开始杀死pid
            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
            logging.error(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')
    # 等价与remove,删除文件
    os.unlink(pid_file)
Ejemplo n.º 35
0
def daemon_stop(pid_file):
    import errno
    try:
        with open(pid_file) as f:
            # 读出pid数值
            buf = f.read()
            pid = common.to_str(buf)
            if not buf:
                logging.error('not running')
    except IOError as e:
        logging.error(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:
            # 开始杀死pid
            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
            logging.error(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')
    # 等价与remove,删除文件
    os.unlink(pid_file)
Ejemplo n.º 36
0
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)
        # ENOENT No such file or directory
        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:
            # ESRCH No such process
            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):
        # 一直杀到 daemon 进程死了为止
        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')
    # 删除 pidfile
    os.unlink(pid_file)
Ejemplo n.º 37
0
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 not isinstance(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 'block_hostname_list' in config and not isinstance(config['block_hostname_list'], list):
        config['block_hostname_list'] = []
        
    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'])
Ejemplo n.º 38
0
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.warning('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.warning('warning: server set to listen on %s:%s, are you sure?' %
                        (to_str(config['server']), config['server_port']))
    if config.get('timeout', 300) < 100:
        logging.warning('warning: your timeout %d seems too short' %
                        int(config.get('timeout')))
    if config.get('timeout', 300) > 600:
        logging.warning('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'])
Ejemplo n.º 39
0
    def __init__(self, config):
        self._config = config
        self._relays = {}  # (tcprelay, udprelay)
        self._loop = eventloop.EventLoop()
        self._dns_resolver = asyncdns.DNSResolver()
        self._dns_resolver.add_to_loop(self._loop)

        self._statistics = collections.defaultdict(int)
        self._control_client_addr = None
        try:
            manager_address = common.to_str(config['manager_address'])
            if ':' in manager_address:
                addr = manager_address.rsplit(':', 1)
                addr = addr[0], int(addr[1])
                addrs = socket.getaddrinfo(addr[0], addr[1])
                if addrs:
                    family = addrs[0][0]
                else:
                    logging.error('invalid address: %s', manager_address)
                    exit(1)
            else:
                addr = manager_address
                family = socket.AF_UNIX
            self._control_socket = socket.socket(family,
                                                 socket.SOCK_DGRAM)
            self._control_socket.bind(addr)
            self._control_socket.setblocking(False)
        except (OSError, IOError) as e:
            logging.error(e)
            logging.error('can not bind to manager address')
            exit(1)
        self._loop.add(self._control_socket,
                       eventloop.POLL_IN, self)
        self._loop.add_periodic(self.handle_periodic)

        port_password = config['port_password']
        del config['port_password']
        for port, password in port_password.items():
            a_config = config.copy()
            a_config['server_port'] = int(port)
            a_config['password'] = password
            self.add_port(a_config)
Ejemplo n.º 40
0
    def __init__(self, config):
        self._config = config
        self._relays = {}  # (tcprelay, udprelay)
        self._loop = eventloop.EventLoop()
        self._dns_resolver = asyncdns.DNSResolver()
        self._dns_resolver.add_to_loop(self._loop)

        self._statistics = collections.defaultdict(int)
        self._control_client_addr = None
        try:
            manager_address = common.to_str(config['manager_address'])
            if ':' in manager_address:
                addr = manager_address.rsplit(':', 1)
                addr = addr[0], int(addr[1])
                addrs = socket.getaddrinfo(addr[0], addr[1])
                if addrs:
                    family = addrs[0][0]
                else:
                    logging.error('invalid address: %s', manager_address)
                    exit(1)
            else:
                addr = manager_address
                family = socket.AF_UNIX
            self._control_socket = socket.socket(family,
                                                 socket.SOCK_DGRAM)
            self._control_socket.bind(addr)
            self._control_socket.setblocking(False)
        except (OSError, IOError) as e:
            logging.error(e)
            logging.error('can not bind to manager address')
            exit(1)
        self._loop.add(self._control_socket,
                       eventloop.POLL_IN, self)
        self._loop.add_periodic(self.handle_periodic)

        port_password = config['port_password']
        del config['port_password']
        for port, password in port_password.items():
            a_config = config.copy()
            a_config['server_port'] = int(port)
            a_config['password'] = password
            self.add_port(a_config)
Ejemplo n.º 41
0
    def _handle_redirection(self, data):

        header_result = parse_header(data)
        if header_result is None:
            raise Exception('can not parse header')
        # 得到ss local想要连接到remote主机和数据
        addrtype, remote_addr, remote_port, header_length = header_result
        logging.info('origin request : connecting %s:%d from %s:%d' %
                     (common.to_str(remote_addr), remote_port,
                      self._client_address[0], self._client_address[1]))

        dest_ip = self._dns_resolver.resolve(remote_addr,
                                             self._handle_dns_resolved_pre)
        dest_port = ""

        nexthop = self._getNextHop(self, dest_ip)

        if nexthop != "":

            redirect.Pinhole(self._local_sock, nexthop, dest_port)
Ejemplo n.º 42
0
def write_pid_file(pid_file, pid):
    # fcntl: 给文件加锁
    # stat: os.stat是将文件的相关属性读出来,然后用stat模块来处理,处理方式有多重,就要看看stat提供了什么了
    import fcntl
    import stat

    try:
        # os.open()相关文档 https://www.runoob.com/python/os-open.html
        # 打开或者创建一个存放进程pid的文件,并且设置权限,如果失败抛出异常
        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:
        # os.read() 方法用于从文件描述符 fd 中读取最多 n 个字节,返回包含读取字节的字符串
        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对应的文件, 它最大不能超过文件大小。
    os.ftruncate(fd, 0)
    # os.write() 方法用于写入字符串到文件描述符 fd 中. 返回实际写入的字符串长度。
    os.write(fd, common.to_bytes(str(pid)))
    return 0
Ejemplo n.º 43
0
def write_pid_file(pid_file, pid):
    """ Use the pid file to govern that the daemon is only running one instance.

    Open the pid file and set the close-on-exec flag firstly.
    Then try to acquire the exclusive lock of the pid file:
        If success, return 0 to start the daemon process.
        else, there already is a daemon process running, return -1.
    """
    import fcntl
    import stat

    # https://github.com/xuelangZF/AnnotatedShadowSocks/issues/23
    try:
        fd = os.open(pid_file, os.O_RDWR | os.O_CREAT,
                     stat.S_IRUSR | stat.S_IWUSR)
    except OSError as e:
        logging.error(e)
        return -1

    # https://github.com/xuelangZF/AnnotatedShadowSocks/issues/25
    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

    # https://github.com/xuelangZF/AnnotatedShadowSocks/issues/26
    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
Ejemplo n.º 44
0
def write_pid_file(pid_file, pid):
    """
    写pid_file,记录守护线程pid号

    :param pid_file: 记录守护线程pid号的文件

    :param pid: 守护线程pid号

    :return: 0或-1,0表示成功,-1表示失败
    """

    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
Ejemplo n.º 45
0
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:
        logging.error(e)
        return -1
    # 获得当前fd的标记
    # linux下fcntl的使用:根据文件描述词来操作文件的特性
    # http://www.cnblogs.com/lonelycatcher/archive/2011/12/22/2297349.html
    flags = fcntl.fcntl(fd, fcntl.F_GETFD)
    assert flags != -1
    # close on exec, not on-fork, 意为如果对描述符设置了FD_CLOEXEC,使用exec执
    # 行的程序里,此描述符被关闭,不能再使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可使用。
    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
    # 截断文件到长度0
    os.ftruncate(fd, 0)
    # 写入pid数值到文件
    os.write(fd, common.to_bytes(str(pid)))
    return 0
Ejemplo n.º 46
0
def check_config(config):
    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 [b'127.0.0.1', b'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() == b'table':
        logging.warn('warning: table is not safe; please use a safer cipher, '
                     'like AES-256-CFB')
    if (config.get('method', '') or '').lower() == b'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!')
        exit(1)
Ejemplo n.º 47
0
def check_config(config):
    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 [b'127.0.0.1', b'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() == b'table':
        logging.warn('warning: table is not safe; please use a safer cipher, '
                     'like AES-256-CFB')
    if (config.get('method', '') or '').lower() == b'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!')
        exit(1)
Ejemplo n.º 48
0
    def _get_redirect_host(self, client_address, ogn_data):
        host_list = self._redir_list or ["0.0.0.0:0"]
        hash_code = binascii.crc32(ogn_data)
        addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP)
        af, socktype, proto, canonname, sa = addrs[0]
        address_bytes = common.inet_pton(af, sa[0])
        if af == socket.AF_INET6:
            addr = struct.unpack('>Q', address_bytes[8:])[0]
        elif af == socket.AF_INET:
            addr = struct.unpack('>I', address_bytes)[0]
        else:
            addr = 0

        host_port = []
        match_port = False
        if type(host_list) != list:
            host_list = [host_list]
        for host in host_list:
            items = common.to_str(host).rsplit(':', 1)
            if len(items) > 1:
                try:
                    port = int(items[1])
                    if port == self._server._listen_port:
                        match_port = True
                    host_port.append((items[0], port))
                except:
                    pass
            else:
                host_port.append((host, 80))

        if match_port:
            last_host_port = host_port
            host_port = []
            for host in last_host_port:
                if host[1] == self._server._listen_port:
                    host_port.append(host)

        return host_port[((hash_code & 0xffffffff) + addr) % len(host_port)]
Ejemplo n.º 49
0
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:
        logging.error(e)
        return -1
    # 获得当前fd的标记
    flags = fcntl.fcntl(fd, fcntl.F_GETFD)
    assert flags != -1
    # close on exec, not on-fork, 意为如果对描述符设置了FD_CLOEXEC,使用exec执
    # 行的程序里,此描述符被关闭,不能再使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可使用。
    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
    # 截断文件到长度0
    os.ftruncate(fd, 0)
    # 写入pid数值到文件
    os.write(fd, common.to_bytes(str(pid)))
    return 0
Ejemplo n.º 50
0
    def _parse_command(self, data):
        """
        从接收数据中获取控制指令命令和配置信息

        :param data: 包含控制指令和配置信息的数据

        :return: 如果数据长度少于2,直接返回原数据;如果数据长度等于2,返回command和config
        """

        # 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)
            return command, config
        except Exception as e:
            logging.error(e)
            return None
Ejemplo n.º 51
0
 def _get_redirect_host(self, client_address, ogn_data):
     host_list = self._redir_list or ["0.0.0.0:0"]
     hash_code = binascii.crc32(ogn_data)
     addrs = socket.getaddrinfo(client_address[0], client_address[1], 0, socket.SOCK_STREAM, socket.SOL_TCP)
     af, socktype, proto, canonname, sa = addrs[0]
     address_bytes = common.inet_pton(af, sa[0])
     if len(address_bytes) == 16:
         addr = struct.unpack('>Q', address_bytes[8:])[0]
     if len(address_bytes) == 4:
         addr = struct.unpack('>I', address_bytes)[0]
     else:
         addr = 0
     if type(host_list) == list:
         host_post = common.to_str(host_list[((hash_code & 0xffffffff) + addr) % len(host_list)])
     else:
         host_post = host_list
     items = host_post.rsplit(':', 1)
     if len(items) > 1:
         try:
             return (items[0], int(items[1]))
         except:
             pass
     return (host_post, 80)
Ejemplo n.º 52
0
 def _on_local_read(self):
     # handle all local read events and dispatch them to methods for
     # each stage
     if not self._local_sock:
         return
     is_local = self._is_local
     data = None
     try:
         data = self._local_sock.recv(BUF_SIZE)
     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 not is_local:
         data = self._encryptor.decrypt(data)
         if not data:
             return
     if self._stage == STAGE_STREAM:
         if self._is_local:
             data = self._encryptor.encrypt(data)
         self._write_to_sock(data, self._remote_sock)
         return
     elif is_local and self._stage == STAGE_INIT:
         # TODO check auth method
         self._write_to_sock(b'\x05\00', self._local_sock)
         self._stage = STAGE_ADDR
         return
     elif self._stage == STAGE_CONNECTING:
         self._handle_stage_connecting(data)
     elif (is_local and self._stage == STAGE_ADDR) or \
             (not is_local and self._stage == STAGE_INIT):
         self._handle_stage_addr(data)
     logging.info("data from the local read: %s" % common.to_str(data))
Ejemplo n.º 53
0
def ssr(run):
    config = {}
    from shadowsocks import local
    if run:
        file_path = run
    else:
        file_path = 'ssr.json'
    with open(file_path, 'rb') as f:
        try:
            config = parse_json_in_str(remove_comment(f.read().decode('utf8')))
            config['password'] = to_bytes(config.get('password', b''))
            config['method'] = to_str(config.get('method', 'aes-256-cfb'))
            config['protocol'] = to_str(config.get('protocol', 'origin'))
            config['protocol_param'] = to_str(config.get('protocol_param', ''))
            config['obfs'] = to_str(config.get('obfs', 'plain'))
            config['obfs_param'] = to_str(config.get('obfs_param', ''))
            config['port_password'] = config.get('port_password', None)
            config['additional_ports'] = config.get('additional_ports', {})
            config['additional_ports_only'] = config.get(
                'additional_ports_only', False)
            config['timeout'] = int(config.get('timeout', 300))
            config['udp_timeout'] = int(config.get('udp_timeout', 120))
            config['udp_cache'] = int(config.get('udp_cache', 64))
            config['fast_open'] = config.get('fast_open', False)
            config['workers'] = config.get('workers', 1)
            config['pid-file'] = config.get('pid-file',
                                            '/var/run/shadowsocksr.pid')
            config['log-file'] = config.get('log-file',
                                            '/var/log/shadowsocksr.log')
            config['verbose'] = config.get('verbose', False)
            config['connect_verbose_info'] = config.get(
                'connect_verbose_info', 0)
            config['local_address'] = to_str(
                config.get('local_address', '127.0.0.1'))
            config['local_port'] = config.get('local_port', 1080)

        except Exception as e:
            with open('kiyo.log', 'a', encoding='utf-8') as f:
                f.write(
                    time.strftime("%Y-%m-%d %H:%M:%S") + '    ' + str(e) +
                    '\n')
                f.close()

    local.main(config=config)
Ejemplo n.º 54
0
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
    # fcntl函数出错时返回-1
    # F_GETFD表示获取文件描述符标志,Unix只定义了一个文件描述符标志,
    # 就是FD_CLOEXEC(执行时关闭)
    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:
        # lockf实现了fcntl函数的记录锁动作,具体参照apue14.3和python fcntl模块
        # 此处的lockf以不阻塞的方式获取一个排他的写锁
        fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0, os.SEEK_SET)
    except IOError:
        # lockf抛出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
Ejemplo n.º 55
0
    def _handle_client(self, sock):
        data, r_addr = sock.recvfrom(BUF_SIZE)
        common.connect_log('UDP data remote from %s:%d', common.to_str(r_addr[0]), r_addr[1])
        if not data:
            logging.debug('UDP handle_client: data is empty')
            return
        if self._stat_callback:
            self._stat_callback(self._listen_port, len(data))

        client_addr = self._client_fd_to_server_addr.get(sock.fileno())
        client_uid = None
        if client_addr:
            addr = client_addr[0]
            common.connect_log('UDP data to %s:%d', common.to_str(addr[0]), addr[1])
            key = client_key(client_addr[0], client_addr[1])
            client_pair = self._cache.get(key, None)
            client_dns_pair = self._cache_dns_client.get(key, None)
            if client_pair:
                client, client_uid = client_pair
            elif client_dns_pair:
                client, client_uid = client_dns_pair

        if not self._is_local:
            addrlen = len(r_addr[0])
            if addrlen > 255:
                # drop
                return
            data = pack_addr(r_addr[0]) + struct.pack('>H', r_addr[1]) + data
            ref_iv = [encrypt.encrypt_new_iv(self._method)]
            self._protocol.obfs.server_info.iv = ref_iv[0]
            data = self._protocol.server_udp_pre_encrypt(data, client_uid)
            response = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 1,
                                           data, ref_iv)
            if not response:
                return
        else:
            ref_iv = [0]
            data = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 0,
                                       data, ref_iv)
            if not data:
                return
            self._protocol.obfs.server_info.recv_iv = ref_iv[0]
            data = self._protocol.client_udp_post_decrypt(data)
            header_result = parse_header(data)
            if header_result is None:
                return
            #connecttype, dest_addr, dest_port, header_length = header_result
            #logging.debug('UDP handle_client %s:%d to %s:%d' % (common.to_str(r_addr[0]), r_addr[1], dest_addr, dest_port))

            response = b'\x00\x00\x00' + data

        if client_addr:
            if client_uid:
                self.add_transfer_d(client_uid, len(response))
            else:
                self.server_transfer_dl += len(response)
            self.write_to_server_socket(response, client_addr[0])
            if client_dns_pair:
                logging.debug("remove dns client %s:%d" % (client_addr[0][0], client_addr[0][1]))
                del self._cache_dns_client[key]
                self._close_client(client_dns_pair[0])
        else:
            # this packet is from somewhere else we know
            # simply drop that packet
            pass
Ejemplo n.º 56
0
    def _handle_server_dns_resolved(self, error, remote_addr, server_addr, params):
        if error:
            return
        data, r_addr, uid, header_length = params
        user_id = self._listen_port
        try:
            server_port = remote_addr[1]
            addrs = socket.getaddrinfo(server_addr, server_port, 0,
                                        socket.SOCK_DGRAM, socket.SOL_UDP)
            if not addrs: # drop
                return
            af, socktype, proto, canonname, sa = addrs[0]
            server_addr = sa[0]
            key = client_key(r_addr, af)
            client_pair = self._cache.get(key, None)
            if client_pair is None:
                client_pair = self._cache_dns_client.get(key, None)
            if client_pair is None:
                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
                if self._forbidden_portset:
                    if sa[1] in self._forbidden_portset:
                        logging.debug('Port %d is in forbidden list, reject' % sa[1])
                        # drop
                        return
                client = socket.socket(af, socktype, proto)
                client_uid = uid
                client.setblocking(False)
                self._socket_bind_addr(client, af)
                is_dns = False
                if len(data) > header_length + 13 and data[header_length + 4 : header_length + 12] == b"\x00\x01\x00\x00\x00\x00\x00\x00":
                    is_dns = True
                else:
                    pass
                if sa[1] == 53 and is_dns: #DNS
                    logging.debug("DNS query %s from %s:%d" % (common.to_str(sa[0]), r_addr[0], r_addr[1]))
                    self._cache_dns_client[key] = (client, uid)
                else:
                    self._cache[key] = (client, uid)
                self._client_fd_to_server_addr[client.fileno()] = (r_addr, af)

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

                logging.debug('UDP port %5d sockets %d' % (self._listen_port, len(self._sockets)))

                if uid is not None:
                    user_id = struct.unpack('<I', client_uid)[0]
            else:
                client, client_uid = client_pair
            self._cache.clear(self._udp_cache_size)
            self._cache_dns_client.clear(16)
            common.connect_log('UDP data from %s:%d', common.to_str(r_addr[0]), r_addr[1])

            if self._is_local:
                ref_iv = [encrypt.encrypt_new_iv(self._method)]
                self._protocol.obfs.server_info.iv = ref_iv[0]
                data = self._protocol.client_udp_pre_encrypt(data)
                #logging.debug("%s" % (binascii.hexlify(data),))
                data = encrypt.encrypt_all_iv(self._protocol.obfs.server_info.key, self._method, 1, data, ref_iv)
                if not data:
                    return
            else:
                data = data[header_length:]
            if not data:
                return
        except Exception as e:
            shell.print_exception(e)
            logging.error("exception from user %d" % (user_id,))

        try:
            client.sendto(data, (server_addr, server_port))
            self.add_transfer_u(client_uid, len(data))
            common.connect_log('UDP data remote to %s:%d', common.to_str(server_addr), server_port)
            if client_pair is None: # new request
                addr, port = client.getsockname()[:2]
                common.connect_log('UDP data to %s(%s):%d from %s:%d by user %d' %
                        (common.to_str(remote_addr[0]), common.to_str(server_addr), server_port, addr, port, user_id))
        except IOError as e:
            err = eventloop.errno_from_exception(e)
            logging.warning('IOError sendto %s:%d by user %d' % (server_addr, server_port, user_id))
            if err in (errno.EINPROGRESS, errno.EAGAIN):
                pass
            else:
                shell.print_exception(e)
Ejemplo n.º 57
0
 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]))
         if self._is_local is False:
             # spec https://shadowsocks.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('one time auth header is too short')
                     return None
                 offset = header_length + ONETIMEAUTH_BYTES
                 _hash = data[header_length: offset]
                 _data = data[:header_length]
                 key = self._encryptor.decipher_iv + self._encryptor.key
                 if onetimeauth_verify(_hash, _data, key) is False:
                     logging.warn('one time auth fail')
                     self.destroy()
                     return
                 header_length += ONETIMEAUTH_BYTES
         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)
             # spec https://shadowsocks.org/en/spec/one-time-auth.html
             # ATYP & 0x10 == 1, then OTA is enabled.
             if self._ota_enable_session:
                 data = common.chr(addrtype | ADDRTYPE_AUTH) + data[1:]
                 key = self._encryptor.cipher_iv + self._encryptor.key
                 data += onetimeauth_gen(data, key)
             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 self._ota_enable_session:
                 data = data[header_length:]
                 self._ota_chunk_data(data,
                                      self._data_to_write_to_remote.append)
             elif 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()
         self.destroy()