Пример #1
0
 def pack_auth_data(self, auth_data, buf):
     if len(buf) == 0:
         return b''
     if len(buf) > 400:
         rnd_len = struct.unpack('<H', os.urandom(2))[0] % 512
     else:
         rnd_len = struct.unpack('<H', os.urandom(2))[0] % 1024
     data = auth_data
     data_len = 7 + 4 + 16 + 4 + len(buf) + rnd_len + 4
     data = data + struct.pack('<H', data_len) + struct.pack('<H', rnd_len)
     mac_key = self.server_info.iv + self.server_info.key
     uid = os.urandom(4)
     if b':' in to_bytes(self.server_info.protocol_param):
         try:
             items = to_bytes(self.server_info.protocol_param).split(b':')
             self.user_key = self.hashfunc(items[1]).digest()
             uid = struct.pack('<I', int(items[0]))
         except:
             pass
     if self.user_key is None:
         self.user_key = self.server_info.key
     encryptor = encrypt.Encryptor(
         to_bytes(base64.b64encode(self.user_key)) + self.salt,
         'aes-128-cbc', b'\x00' * 16)
     data = uid + encryptor.encrypt(data)[16:]
     data += hmac.new(mac_key, data, self.hashfunc).digest()[:4]
     check_head = os.urandom(1)
     check_head += hmac.new(mac_key, check_head, self.hashfunc).digest()[:6]
     data = check_head + data + os.urandom(rnd_len) + buf
     data += hmac.new(self.user_key, data, self.hashfunc).digest()[:4]
     return data
Пример #2
0
 def client_encode(self, buf):
     if self.has_sent_header:
         return buf
     head_size = len(self.server_info.iv) + self.server_info.head_len
     if len(buf) - head_size > 64:
         headlen = head_size + random.randint(0, 64)
     else:
         headlen = len(buf)
     headdata = buf[:headlen]
     buf = buf[headlen:]
     port = b''
     if self.server_info.port != 80:
         port = b':' + to_bytes(str(self.server_info.port))
     body = None
     hosts = (self.server_info.obfs_param or self.server_info.host)
     pos = hosts.find("#")
     if pos >= 0:
         body = hosts[pos + 1:].replace("\n", "\r\n")
         body = body.replace("\\n", "\r\n")
         hosts = hosts[:pos]
     hosts = hosts.split(',')
     host = random.choice(hosts)
     http_head = b"GET /" + self.encode_head(headdata) + b" HTTP/1.1\r\n"
     http_head += b"Host: " + to_bytes(host) + port + b"\r\n"
     if body:
         http_head += body + "\r\n\r\n"
     else:
         http_head += b"User-Agent: " + random.choice(
             self.user_agent) + b"\r\n"
         http_head += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive\r\n\r\n"
     self.has_sent_header = True
     return http_head + buf
Пример #3
0
 def client_udp_pre_encrypt(self, buf):
     if self.user_key is None:
         if b':' in to_bytes(self.server_info.protocol_param):
             try:
                 items = to_bytes(
                     self.server_info.protocol_param).split(':')
                 self.user_key = self.hashfunc(items[1]).digest()
                 self.user_id = struct.pack('<I', int(items[0]))
             except:
                 pass
         if self.user_key is None:
             self.user_id = os.urandom(4)
             self.user_key = self.server_info.key
     authdata = os.urandom(3)
     mac_key = self.server_info.key
     md5data = hmac.new(mac_key, authdata, self.hashfunc).digest()
     uid = struct.unpack('<I', self.user_id)[0] ^ struct.unpack(
         '<I', md5data[:4])[0]
     uid = struct.pack('<I', uid)
     rand_len = self.udp_rnd_data_len(md5data, self.random_client)
     encryptor = encrypt.Encryptor(
         to_bytes(base64.b64encode(self.user_key)) +
         to_bytes(base64.b64encode(md5data)), 'rc4')
     out_buf = encryptor.encrypt(buf)
     buf = out_buf + os.urandom(rand_len) + authdata + uid
     return buf + hmac.new(self.user_key, buf, self.hashfunc).digest()[:1]
Пример #4
0
 def client_udp_post_decrypt(self, buf):
     if len(buf) <= 8:
         return (b'', None)
     if hmac.new(self.user_key, buf[:-1],
                 self.hashfunc).digest()[:1] != buf[-1:]:
         return (b'', None)
     mac_key = self.server_info.key
     md5data = hmac.new(mac_key, buf[-8:-1], self.hashfunc).digest()
     rand_len = self.udp_rnd_data_len(md5data, self.random_server)
     encryptor = encrypt.Encryptor(
         to_bytes(base64.b64encode(self.user_key)) +
         to_bytes(base64.b64encode(md5data)), 'rc4')
     return encryptor.decrypt(buf[:-8 - rand_len])
Пример #5
0
 def client_udp_pre_encrypt(self, buf):
     if self.user_key is None:
         if b':' in to_bytes(self.server_info.protocol_param):
             try:
                 items = to_bytes(
                     self.server_info.protocol_param).split(':')
                 self.user_key = self.hashfunc(items[1]).digest()
                 self.user_id = struct.pack('<I', int(items[0]))
             except:
                 pass
         if self.user_key is None:
             self.user_id = os.urandom(4)
             self.user_key = self.server_info.key
     buf += self.user_id
     return buf + hmac.new(self.user_key, buf, self.hashfunc).digest()[:4]
Пример #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:
        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
    # TODO: use kill(0) for the pid in pid_file if the file exists

    # 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
Пример #7
0
 def sni(self, url):
     url = common.to_bytes(url)
     data = b"\x00" + struct.pack('>H', len(url)) + url
     data = b"\x00\x00" + struct.pack('>H',
                                      len(data) + 2) + struct.pack(
                                          '>H', len(data)) + data
     return data
Пример #8
0
 def server_udp_pre_encrypt(self, buf, uid):
     if uid in self.server_info.users:
         user_key = self.server_info.users[uid]
     else:
         uid = None
         if not self.server_info.users:
             user_key = self.server_info.key
         else:
             user_key = self.server_info.recv_iv
     authdata = os.urandom(7)
     mac_key = self.server_info.key
     md5data = hmac.new(mac_key, authdata, self.hashfunc).digest()
     rand_len = self.udp_rnd_data_len(md5data, self.random_server)
     encryptor = encrypt.Encryptor(
         to_bytes(base64.b64encode(user_key)) +
         to_bytes(base64.b64encode(md5data)), 'rc4')
     out_buf = encryptor.encrypt(buf)
     buf = out_buf + os.urandom(rand_len) + authdata
     return buf + hmac.new(user_key, buf, self.hashfunc).digest()[:1]
Пример #9
0
def base64_decode(string):
    def adjust_padding(string):
        """Adjust to base64 format, i.e. len(string) % 4 == 0."""
        missing_padding = len(string) % 4
        if missing_padding:
            string += '=' * (4 - missing_padding)
        return string

    string = adjust_padding(string.strip())
    return common.to_str(base64.urlsafe_b64decode(common.to_bytes(string)))
Пример #10
0
    def server_encode(self, buf):
        if self.has_sent_header:
            return buf

        header = b'HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: '
        header += to_bytes(
            datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT'))
        header += b'\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n'
        self.has_sent_header = True
        return header + buf
Пример #11
0
    def pack_auth_data(self, auth_data, buf):
        data = auth_data
        data_len = 12 + 4 + 16 + 4
        data = data + (struct.pack('<H', self.server_info.overhead) +
                       struct.pack('<H', 0))
        mac_key = self.server_info.iv + self.server_info.key

        check_head = os.urandom(4)
        self.last_client_hash = hmac.new(mac_key, check_head,
                                         self.hashfunc).digest()
        check_head += self.last_client_hash[:8]

        if b':' in to_bytes(self.server_info.protocol_param):
            try:
                items = to_bytes(self.server_info.protocol_param).split(b':')
                self.user_key = items[1]
                uid = struct.pack('<I', int(items[0]))
            except:
                uid = os.urandom(4)
        else:
            uid = os.urandom(4)
        if self.user_key is None:
            self.user_key = self.server_info.key

        encryptor = encrypt.Encryptor(
            to_bytes(base64.b64encode(self.user_key)) + self.salt,
            'aes-128-cbc', b'\x00' * 16)

        uid = struct.unpack('<I', uid)[0] ^ struct.unpack(
            '<I', self.last_client_hash[8:12])[0]
        uid = struct.pack('<I', uid)
        data = uid + encryptor.encrypt(data)[16:]
        self.last_server_hash = hmac.new(self.user_key, data,
                                         self.hashfunc).digest()
        data = check_head + data + self.last_server_hash[:4]
        self.encryptor = encrypt.Encryptor(
            to_bytes(base64.b64encode(self.user_key)) +
            to_bytes(base64.b64encode(self.last_client_hash)), 'rc4')
        return data + self.pack_client_data(buf)
Пример #12
0
 def server_udp_post_decrypt(self, buf):
     mac_key = self.server_info.key
     md5data = hmac.new(mac_key, buf[-8:-5], self.hashfunc).digest()
     uid = struct.unpack('<I', buf[-5:-1])[0] ^ struct.unpack(
         '<I', md5data[:4])[0]
     uid = struct.pack('<I', uid)
     if uid in self.server_info.users:
         user_key = self.server_info.users[uid]
     else:
         uid = None
         if not self.server_info.users:
             user_key = self.server_info.key
         else:
             user_key = self.server_info.recv_iv
     if hmac.new(user_key, buf[:-1],
                 self.hashfunc).digest()[:1] != buf[-1:]:
         return (b'', None)
     rand_len = self.udp_rnd_data_len(md5data, self.random_client)
     encryptor = encrypt.Encryptor(
         to_bytes(base64.b64encode(user_key)) +
         to_bytes(base64.b64encode(md5data)), 'rc4')
     out_buf = encryptor.decrypt(buf[:-8 - rand_len])
     return (out_buf, uid)
Пример #13
0
    def get_cipher(self, password, method, op, iv):
        password = common.to_bytes(password)
        m = self._method_info
        if m[0] > 0:
            key, iv_ = EVP_BytesToKey(password, m[0], m[1])
        else:
            # key_length == 0 indicates we should use the key directly
            key, iv = password, b''

        iv = iv[:m[1]]
        if op == 1:
            # this iv is for cipher not decipher
            self.cipher_iv = iv[:m[1]]
        self.cipher_key = key
        return m[2](method, key, iv, op)
Пример #14
0
 def _update_users(self, protocol_param, acl):
     if protocol_param is None:
         protocol_param = self._config['protocol_param']
     param = common.to_bytes(protocol_param).split(b'#')
     if len(param) == 2:
         user_list = param[1].split(b',')
         if user_list:
             for user in user_list:
                 items = user.split(b':')
                 if len(items) == 2:
                     user_int_id = int(items[0])
                     uid = struct.pack('<I', user_int_id)
                     if acl is not None and user_int_id not in acl:
                         self.del_user(uid)
                     else:
                         passwd = items[1]
                         self.add_user(uid, {'password': passwd})
Пример #15
0
 def __init__(self, cipher_name, key, iv, op):
     self._ctx = None
     if not loaded:
         load_openssl()
     cipher = libcrypto.EVP_get_cipherbyname(common.to_bytes(cipher_name))
     if not cipher:
         cipher = load_cipher(cipher_name)
     if not cipher:
         raise Exception('cipher %s not found in libcrypto' % cipher_name)
     key_ptr = c_char_p(key)
     iv_ptr = c_char_p(iv)
     self._ctx = libcrypto.EVP_CIPHER_CTX_new()
     if not self._ctx:
         raise Exception('can not create cipher context')
     r = libcrypto.EVP_CipherInit_ex(self._ctx, cipher, None,
                                     key_ptr, iv_ptr, c_int(op))
     if not r:
         self.clean()
         raise Exception('can not initialize cipher context')
Пример #16
0
def base64_encode(string):
    return common.to_str(base64.urlsafe_b64encode(
        common.to_bytes(string))).replace('=', '')
Пример #17
0
def parse_config(is_local, config_=None):
    global verbose
    global config
    global config_path
    # config = {}
    # config_path = None
    logging.basicConfig(level=logging.INFO,
                        format='%(levelname)-s: %(message)s')
    if is_local:  # check this is a client or a server.
        shortopts = 'hd:s:b:p:k:l:m:O:o:G:g:c:t:L:vq'
        longopts = [
            'help', 'fast-open', 'link', 'pid-file=', 'log-file=', 'user='******'version'
        ]
    else:
        shortopts = 'hd:s:p:k:m:O:o:G:g:c:t:vq'
        longopts = [
            'help', 'fast-open', 'pid-file=', 'log-file=', 'workers=',
            'forbidden-ip=', 'user='******'manager-address=', 'version'
        ]
    if config_:
        config.update(config_)
    else:
        args = parse_args()[0]

    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['pid-file'] = config.get('pid-file', '/tmp/shadowsocksr.pid')
    config['log-file'] = config.get('log-file', '/tmp/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)
    if is_local:
        # FIXME: enable not provide server addr if daemon stop or restart
        # if config.get('server', None) is None and not args.command and (not args.d or args.d == 'start'):
        # 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'])
        if 'server' in config:
            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:
            logger.error(e)
            sys.exit(2)
        try:
            config['forbidden_port'] = PortRange(
                config.get('forbidden_port', ''))
        except Exception as e:
            logger.error(e)
            sys.exit(2)
        try:
            config['ignore_bind'] = \
                IPNetwork(config.get('ignore_bind', '127.0.0.0/8,::1/128,10.0.0.0/8,192.168.0.0/16'))
        except Exception as e:
            logger.error(e)
            sys.exit(2)
    config['server_port'] = config.get('server_port', 8388)

    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 %(filename)s:%(lineno)s %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S')

    check_config(config, is_local)

    return config
Пример #18
0
    def server_post_decrypt(self, buf):
        if self.raw_trans:
            return (buf, False)
        self.recv_buf += buf
        out_buf = b''
        sendback = False

        if not self.has_recv_header:
            if len(self.recv_buf) >= 7 or len(self.recv_buf) in [2, 3]:
                recv_len = min(len(self.recv_buf), 7)
                mac_key = self.server_info.recv_iv + self.server_info.key
                sha1data = hmac.new(mac_key, self.recv_buf[:1],
                                    self.hashfunc).digest()[:recv_len - 1]
                if sha1data != self.recv_buf[1:recv_len]:
                    return self.not_match_return(self.recv_buf)

            if len(self.recv_buf) < 31:
                return (b'', False)
            sha1data = hmac.new(mac_key, self.recv_buf[7:27],
                                self.hashfunc).digest()[:4]
            if sha1data != self.recv_buf[27:31]:
                logging.error(
                    '%s data uncorrect auth HMAC-SHA1 from %s:%d, data %s' %
                    (self.no_compatible_method, self.server_info.client,
                     self.server_info.client_port,
                     binascii.hexlify(self.recv_buf)))
                if len(self.recv_buf) < 31 + self.extra_wait_size:
                    return (b'', False)
                return self.not_match_return(self.recv_buf)

            uid = self.recv_buf[7:11]
            if uid in self.server_info.users:
                self.user_id = uid
                self.user_key = self.hashfunc(
                    self.server_info.users[uid]).digest()
                self.server_info.update_user_func(uid)
            else:
                if not self.server_info.users:
                    self.user_key = self.server_info.key
                else:
                    self.user_key = self.server_info.recv_iv
            encryptor = encrypt.Encryptor(
                to_bytes(base64.b64encode(self.user_key)) + self.salt,
                'aes-128-cbc')
            head = encryptor.decrypt(
                b'\x00' * 16 + self.recv_buf[11:27] +
                b'\x00')  # need an extra byte or recv empty
            length = struct.unpack('<H', head[12:14])[0]
            if len(self.recv_buf) < length:
                return (b'', False)

            utc_time = struct.unpack('<I', head[:4])[0]
            client_id = struct.unpack('<I', head[4:8])[0]
            connection_id = struct.unpack('<I', head[8:12])[0]
            rnd_len = struct.unpack('<H', head[14:16])[0]
            if hmac.new(self.user_key, self.recv_buf[:length - 4],
                        self.hashfunc).digest()[:4] != self.recv_buf[length -
                                                                     4:length]:
                logging.info('%s: checksum error, data %s' %
                             (self.no_compatible_method,
                              binascii.hexlify(self.recv_buf[:length])))
                return self.not_match_return(self.recv_buf)
            time_dif = common.int32(utc_time - (int(time.time()) & 0xffffffff))
            if time_dif < -self.max_time_dif or time_dif > self.max_time_dif:
                logging.info('%s: wrong timestamp, time_dif %d, data %s' %
                             (self.no_compatible_method, time_dif,
                              binascii.hexlify(head)))
                return self.not_match_return(self.recv_buf)
            elif self.server_info.data.insert(self.user_id, client_id,
                                              connection_id):
                self.has_recv_header = True
                out_buf = self.recv_buf[31 + rnd_len:length - 4]
                self.client_id = client_id
                self.connection_id = connection_id
            else:
                logging.info(
                    '%s: auth fail, data %s' %
                    (self.no_compatible_method, binascii.hexlify(out_buf)))
                return self.not_match_return(self.recv_buf)
            self.recv_buf = self.recv_buf[length:]
            self.has_recv_header = True
            sendback = True

        while len(self.recv_buf) > 4:
            mac_key = self.user_key + struct.pack('<I', self.recv_id)
            mac = hmac.new(mac_key, self.recv_buf[:2],
                           self.hashfunc).digest()[:2]
            if mac != self.recv_buf[2:4]:
                self.raw_trans = True
                logging.info(self.no_compatible_method + ': wrong crc')
                if self.recv_id == 0:
                    logging.info(self.no_compatible_method + ': wrong crc')
                    return (b'E' * 2048, False)
                else:
                    raise Exception('server_post_decrype data error')
            length = struct.unpack('<H', self.recv_buf[:2])[0]
            if length >= 8192 or length < 7:
                self.raw_trans = True
                self.recv_buf = b''
                if self.recv_id == 0:
                    logging.info(self.no_compatible_method + ': over size')
                    return (b'E' * 2048, False)
                else:
                    raise Exception('server_post_decrype data error')
            if length > len(self.recv_buf):
                break

            if hmac.new(mac_key, self.recv_buf[:length - 4],
                        self.hashfunc).digest()[:4] != self.recv_buf[length -
                                                                     4:length]:
                logging.info('%s: checksum error, data %s' %
                             (self.no_compatible_method,
                              binascii.hexlify(self.recv_buf[:length])))
                self.raw_trans = True
                self.recv_buf = b''
                if self.recv_id == 0:
                    return (b'E' * 2048, False)
                else:
                    raise Exception(
                        'server_post_decrype data uncorrect checksum')

            self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
            pos = common.ord(self.recv_buf[4])
            if pos < 255:
                pos += 4
            else:
                pos = struct.unpack('<H', self.recv_buf[5:7])[0] + 4
            out_buf += self.recv_buf[pos:length - 4]
            self.recv_buf = self.recv_buf[length:]
            if pos == length - 4:
                sendback = True

        if out_buf:
            self.server_info.data.update(self.user_id, self.client_id,
                                         self.connection_id)
        return (out_buf, sendback)
Пример #19
0
def parse_args(args_=None):
    # FIXME: called twice, service, parse_config
    def args_error(
            message):  # TODO: print help information when invalid arguments
        nonlocal parser
        sys.stderr.write('error: %s\n'.format(message))
        print('something wrong')
        parser.print_help()

    parser = argparse.ArgumentParser(
        description='A fast tunnel proxy that helps you bypass firewalls.',
        usage='ssclient <command> [OPTION]',
        epilog='Online help: <https://github.com/shadowsocks/shadowsocks>')
    # TODO: add conflicts of -L with others.
    # default to old version config path, if args.command is set, change it to new version config path
    parser.add_argument('-s', metavar='SERVER_ADDR', help='server address')
    parser.add_argument('-p',
                        metavar='SERVER_PORT',
                        help='server port',
                        default='8388')
    parser.add_argument('-k', metavar='PASSWORD', help='password')
    parser.add_argument('-m',
                        metavar='METHOD',
                        help='encryption method',
                        default='aes-256-cfb')
    parser.add_argument('-O',
                        metavar='PROTOCOL',
                        help='protocol',
                        default='http_simple')
    parser.add_argument('-G',
                        metavar='PROTOCOL_PARAM',
                        help='protocol param',
                        default='')
    parser.add_argument('-o',
                        metavar='OBFS',
                        help='obfsplugin',
                        default='http_simple')
    parser.add_argument('-g',
                        metavar='OBFS_PARAM',
                        help='obfs param',
                        default='')

    subparsers = parser.add_subparsers(title='command title',
                                       dest='command',
                                       help='sub-commands',
                                       metavar='<command>')
    server_parser = subparsers.add_parser('server', help='xxx')
    feed_parser = subparsers.add_parser('feed', help='yyy')
    status_parser = subparsers.add_parser('status', help='show current status')
    service_parser = subparsers.add_parser('service',
                                           help='service operations')
    config_parser = subparsers.add_parser('config', help='yyy')

    server_parser.add_argument('subcmd', help='server command')

    feed_parser.add_argument('--link',
                             help='ssr link')  # TODO: if no link, ask later.
    feed_parser.add_argument('subcmd', help='subscription command')
    feed_parser.add_argument('--source', help='souurce address')
    # status_parser.add_argument('subcmd', help='show current status')
    service_parser.add_argument('subcmd', help='show current status')
    config_parser.add_argument('subcmd', help='show current status')
    # config_parser.add_argument('-c', help='path to the import config file')
    config_parser.add_argument('-o', help='path to the :xport config file')

    for p in (parser, server_parser, feed_parser, status_parser, config_parser,
              service_parser):
        p.add_argument('-b',
                       metavar='LOCAL_ADDR',
                       help='local address',
                       default='127.0.0.1')
        p.add_argument('-l',
                       metavar='LOCAL_PORT',
                       help='local port',
                       default='1080')
        p.add_argument('-i',
                       action='store_true',
                       help='start in interactive mode')
        p.add_argument('-c', metavar='CONFIG', help='path to config file')
        p.add_argument('-L', metavar='SSR_LINK', help='connect using ssr link')
        p.add_argument('-t',
                       metavar='TIMEOUT',
                       help='timeout in seconds',
                       default=300)
        p.add_argument('--fast-open',
                       action='store_true',
                       help='use TCP_FAST_OPEN, requires Linux 3.7+')
        # p.add_argument('-d', metavar='', help='daemon mode (start/stop/restart)', choices=['start', 'stop', 'restart'])
        # TODO: change help message about daemon, we don't need stop/restart option now
        p.add_argument('-d',
                       action='store_true',
                       help='daemon mode (start/stop/restart)')
        p.add_argument('--version',
                       help='show version information',
                       action='store_true')
        p.add_argument('--pid-file',
                       metavar='PID_FILE',
                       help='pid file for daemon mode')
        p.add_argument('--log-file',
                       metavar='LOG_FILE',
                       help='log file daemon mode')
        p.add_argument('--user', metavar='USER', help='run as user')
        p.add_argument('--workers', metavar='WORKERS', default=1)
        p.add_argument('-v',
                       '-vv',
                       action='count',
                       help='verbose mode',
                       default=0)
        # TODO: remove quiet mode
        p.add_argument('-q', '-qq', action='count', help='quiet mode')

    try:
        if args_:
            args, extra_args = parser.parse_known_args(args_)
        else:
            args, extra_args = parser.parse_known_args()
    # except argparse.ArgumentParser as e:
    # we cannot catch argparse.ArgumentParser since it does not inherit from BaseException,
    # when errors in args is founded, argparse just raise SystemExit, this is horrible,
    # we really need to handle the exception to stop daemon from exit.
    except SystemExit as e:
        # TODO: add subcommand `help`, and `help` message
        # args = parser.parse_known_args(['-h'])[0]
        args = parser.parse_known_args(['feed', 'list'])[0]
        pass

    global verbose
    global config
    config = {}
    global config_path
    config_path = None
    logging.basicConfig(level=logging.INFO,
                        format='%(levelname)-s: %(message)s')
    if args.version:
        print_shadowsocks()
        # sys.exit(0)

    if args.c:
        # FIXME: enable default config_path
        if args.command:
            # if args.c == 'default':
            #     config_path = find_config(True)
            # else:
            config_path = args.c
        else:
            # if args.c == 'default':
            #     config_path = find_config(False)
            # else:
            config_path = args.c
            logger.debug('loading config from %s' % config_path)
            with open(config_path, 'rb') as f:
                try:
                    config = parse_json_in_str(
                        remove_comment(f.read().decode('utf8')))
                except ValueError as e:
                    logger.error('found an error in config.json: %s', str(e))
                    sys.exit(1)
    if config_path:
        config['config_path'] = config_path
    else:
        config['config_path'] = find_config(args.command)

    if args.p:
        config['server_port'] = int(args.p)
    if args.k:
        config['password'] = to_bytes(args.k)
    if args.l:
        config['local_port'] = int(args.l)
    if args.s:
        config['server'] = to_str(args.s)
    if args.m:
        config['method'] = to_str(args.m)
    if args.O:
        config['protocol'] = to_str(args.O)
    if args.o:
        config['obfs'] = to_str(args.o)
    if args.G:
        config['protocol_param'] = to_str(args.G)
    if args.g:
        config['obfs_param'] = to_str(args.g)
    if args.b:
        config['local_address'] = to_str(args.b)
    if args.t:
        config['timeout'] = int(args.t)
    # FIXME:
    # if key == '--fast-open':
    #     config['fast_open'] = True
    if args.workers:
        config['workers'] = int(args.workers)
    # FIXME:
    # if key == '--manager-address':
    #     config['manager_address'] = value
    if args.user:
        config['user'] = to_str(args.user)
    # FIXME:
    # if key == '--forbidden-ip':
    #     config['forbidden_ip'] = to_str(value)
    if args.d:
        config['daemon'] = to_str(args.d)
    # FIXME:
    # if key == '--pid-file':
    #     config['pid-file'] = to_str(value)
    # FIXME:
    # if key == '--log-file':
    #     config['log-file'] = to_str(value)
    config['verbose'] = args.v
    if args.q:
        config['verbose'] -= 1
    if args.L:
        config_from_ssrlink = decode_ssrlink(args.L)
        # config.update(config_from_ssrlink)
        config_from_ssrlink.update(config)
        config = config_from_ssrlink

    return (args, extra_args)
Пример #20
0
 def send_data(data_dict):
     if data_dict:
         # use compact JSON format (without space)
         data = common.to_bytes(
             json.dumps(data_dict, separators=(',', ':')))
         self._send_control_data(b'stat: ' + data)
Пример #21
0
    def __init__(self,
                 config,
                 dns_resolver,
                 is_local,
                 stat_callback=None,
                 stat_counter=None):
        self._config = config
        if config.get('connect_verbose_info', 0) > 0:
            common.connect_log = logging.info
        if is_local:
            self._listen_addr = config['local_address']
            self._listen_port = config['local_port']
            self._remote_addr = config['server']
            self._remote_port = config['server_port']
        else:
            self._listen_addr = config['server']
            self._listen_port = config['server_port']
            self._remote_addr = None
            self._remote_port = None
        self._dns_resolver = dns_resolver
        self._password = common.to_bytes(config['password'])
        self._method = config['method']
        self._timeout = config['timeout']
        self._is_local = is_local
        self._udp_cache_size = config['udp_cache']
        self._cache = lru_cache.LRUCache(
            timeout=config['udp_timeout'],
            close_callback=self._close_client_pair)
        self._cache_dns_client = lru_cache.LRUCache(
            timeout=10, close_callback=self._close_client_pair)
        self._client_fd_to_server_addr = {}
        #self._dns_cache = lru_cache.LRUCache(timeout=1800)
        self._eventloop = None
        self._closed = False
        self.server_transfer_ul = 0
        self.server_transfer_dl = 0
        self.server_users = {}
        self.server_user_transfer_ul = {}
        self.server_user_transfer_dl = {}

        if common.to_bytes(config['protocol']) in obfs.mu_protocol():
            self._update_users(None, None)

        self.protocol_data = obfs.obfs(config['protocol']).init_data()
        self._protocol = obfs.obfs(config['protocol'])
        server_info = obfs.server_info(self.protocol_data)
        server_info.host = self._listen_addr
        server_info.port = self._listen_port
        server_info.users = self.server_users
        server_info.protocol_param = config['protocol_param']
        server_info.obfs_param = ''
        server_info.iv = b''
        server_info.recv_iv = b''
        server_info.key_str = common.to_bytes(config['password'])
        server_info.key = encrypt.encrypt_key(self._password, self._method)
        server_info.head_len = 30
        server_info.tcp_mss = 1452
        server_info.buffer_size = BUF_SIZE
        server_info.overhead = 0
        self._protocol.set_server_info(server_info)

        self._sockets = set()
        self._fd_to_handlers = {}
        self._reqid_to_hd = {}
        self._data_to_write_to_server_socket = []

        self._timeout_cache = lru_cache.LRUCache(
            timeout=self._timeout, close_callback=self._close_tcp_client)

        self._bind = config.get('out_bind', '')
        self._bindv6 = config.get('out_bindv6', '')
        self._ignore_bind_list = config.get('ignore_bind', [])

        if 'forbidden_ip' in config:
            self._forbidden_iplist = config['forbidden_ip']
        else:
            self._forbidden_iplist = None
        if 'forbidden_port' in config:
            self._forbidden_portset = config['forbidden_port']
        else:
            self._forbidden_portset = None

        addrs = socket.getaddrinfo(self._listen_addr, self._listen_port, 0,
                                   socket.SOCK_DGRAM, socket.SOL_UDP)
        if len(addrs) == 0:
            raise Exception("can't get addrinfo for %s:%d" %
                            (self._listen_addr, self._listen_port))
        af, socktype, proto, canonname, sa = addrs[0]
        server_socket = socket.socket(af, socktype, proto)
        server_socket.bind((self._listen_addr, self._listen_port))
        server_socket.setblocking(False)
        self._server_socket = server_socket
        self._stat_callback = stat_callback
Пример #22
0
 def add_user(self, uid, cfg):  # user: binstr[4], passwd: str
     passwd = cfg['password']
     self.server_users[uid] = common.to_bytes(passwd)
Пример #23
0
    def server_post_decrypt(self, buf):
        if self.raw_trans:
            return (buf, False)
        self.recv_buf += buf
        out_buf = b''
        sendback = False

        if not self.has_recv_header:
            if len(self.recv_buf) >= 12 or len(self.recv_buf) in [7, 8]:
                recv_len = min(len(self.recv_buf), 12)
                mac_key = self.server_info.recv_iv + self.server_info.key
                md5data = hmac.new(mac_key, self.recv_buf[:4],
                                   self.hashfunc).digest()
                if md5data[:recv_len - 4] != self.recv_buf[4:recv_len]:
                    return self.not_match_return(self.recv_buf)

            if len(self.recv_buf) < 12 + 24:
                return (b'', False)

            self.last_client_hash = md5data
            uid = struct.unpack('<I', self.recv_buf[12:16])[0] ^ struct.unpack(
                '<I', md5data[8:12])[0]
            self.user_id_num = uid
            uid = struct.pack('<I', uid)
            if uid in self.server_info.users:
                self.user_id = uid
                self.user_key = self.server_info.users[uid]
                self.server_info.update_user_func(uid)
            else:
                self.user_id_num = 0
                if not self.server_info.users:
                    self.user_key = self.server_info.key
                else:
                    self.user_key = self.server_info.recv_iv

            md5data = hmac.new(self.user_key, self.recv_buf[12:12 + 20],
                               self.hashfunc).digest()
            if md5data[:4] != self.recv_buf[32:36]:
                logging.error(
                    '%s data uncorrect auth HMAC-MD5 from %s:%d, data %s' %
                    (self.no_compatible_method, self.server_info.client,
                     self.server_info.client_port,
                     binascii.hexlify(self.recv_buf)))
                if len(self.recv_buf) < 36:
                    return (b'', False)
                return self.not_match_return(self.recv_buf)

            self.last_server_hash = md5data
            encryptor = encrypt.Encryptor(
                to_bytes(base64.b64encode(self.user_key)) + self.salt,
                'aes-128-cbc')
            head = encryptor.decrypt(
                b'\x00' * 16 + self.recv_buf[16:32] +
                b'\x00')  # need an extra byte or recv empty
            self.client_over_head = struct.unpack('<H', head[12:14])[0]

            utc_time = struct.unpack('<I', head[:4])[0]
            client_id = struct.unpack('<I', head[4:8])[0]
            connection_id = struct.unpack('<I', head[8:12])[0]
            time_dif = common.int32(utc_time - (int(time.time()) & 0xffffffff))
            if time_dif < -self.max_time_dif or time_dif > self.max_time_dif:
                logging.info('%s: wrong timestamp, time_dif %d, data %s' %
                             (self.no_compatible_method, time_dif,
                              binascii.hexlify(head)))
                return self.not_match_return(self.recv_buf)
            elif self.server_info.data.insert(self.user_id, client_id,
                                              connection_id):
                self.has_recv_header = True
                self.client_id = client_id
                self.connection_id = connection_id
            else:
                logging.info(
                    '%s: auth fail, data %s' %
                    (self.no_compatible_method, binascii.hexlify(out_buf)))
                return self.not_match_return(self.recv_buf)

            self.encryptor = encrypt.Encryptor(
                to_bytes(base64.b64encode(self.user_key)) +
                to_bytes(base64.b64encode(self.last_client_hash)), 'rc4')
            self.recv_buf = self.recv_buf[36:]
            self.has_recv_header = True
            sendback = True

        while len(self.recv_buf) > 4:
            mac_key = self.user_key + struct.pack('<I', self.recv_id)
            data_len = struct.unpack('<H',
                                     self.recv_buf[:2])[0] ^ struct.unpack(
                                         '<H', self.last_client_hash[14:16])[0]
            rand_len = self.rnd_data_len(data_len, self.last_client_hash,
                                         self.random_client)
            length = data_len + rand_len
            if length >= 4096:
                self.raw_trans = True
                self.recv_buf = b''
                if self.recv_id == 0:
                    logging.info(self.no_compatible_method + ': over size')
                    return (b'E' * 2048, False)
                else:
                    raise Exception('server_post_decrype data error')

            if length + 4 > len(self.recv_buf):
                break

            client_hash = hmac.new(mac_key, self.recv_buf[:length + 2],
                                   self.hashfunc).digest()
            if client_hash[:2] != self.recv_buf[length + 2:length + 4]:
                logging.info('%s: checksum error, data %s' %
                             (self.no_compatible_method,
                              binascii.hexlify(self.recv_buf[:length])))
                self.raw_trans = True
                self.recv_buf = b''
                if self.recv_id == 0:
                    return (b'E' * 2048, False)
                else:
                    raise Exception(
                        'server_post_decrype data uncorrect checksum')

            self.recv_id = (self.recv_id + 1) & 0xFFFFFFFF
            pos = 2
            if data_len > 0 and rand_len > 0:
                pos = 2 + self.rnd_start_pos(rand_len, self.random_client)
            out_buf += self.encryptor.decrypt(self.recv_buf[pos:data_len +
                                                            pos])
            self.last_client_hash = client_hash
            self.recv_buf = self.recv_buf[length + 4:]
            if data_len == 0:
                sendback = True

        if out_buf:
            self.server_info.data.update(self.user_id, self.client_id,
                                         self.connection_id)
        return (out_buf, sendback)
Пример #24
0
 def boundary(self):
     return to_bytes(''.join([
         random.choice(
             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
         ) for i in range(32)
     ]))