Example #1
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:])
Example #2
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
Example #3
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)
Example #4
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 ""
Example #5
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)))
Example #6
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:
         logger.error(e)
         return None
Example #7
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):
        logger.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):
        logger.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']:
        logger.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']:
        logger.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:
        logger.warning('warning: your timeout %d seems too short' %
                       int(config.get('timeout')))
    if config.get('timeout', 300) > 600:
        logger.warning('warning: your timeout %d seems too long' %
                       int(config.get('timeout')))
    if config.get('password') in [b'mypassword']:
        logger.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':
            logger.error('user can be used only on Unix')
            sys.exit(1)

    encrypt.try_cipher(config['password'], config['method'])
Example #8
0
def daemon_stop(pid_file=_default_pid_file):
    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)
Example #9
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:
                    logger.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:
            logger.error(e)
            logger.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)
Example #10
0
def main():
    shell.check_python()

    shell.parse_args()
    config = shell.parse_config(False)

    shell.log_shadowsocks_version()

    daemon.daemon_exec(config)

    try:
        import resource
        logging.info(
            'current process RLIMIT_NOFILE resource: soft %d hard %d' %
            resource.getrlimit(resource.RLIMIT_NOFILE))
    except ImportError:
        pass

    if config['port_password']:
        pass
    else:
        config['port_password'] = {}
        server_port = config['server_port']
        if type(server_port) == list:
            for a_server_port in server_port:
                config['port_password'][a_server_port] = config['password']
        else:
            config['port_password'][str(server_port)] = config['password']

    if not config.get('dns_ipv6', False):
        asyncdns.IPV6_CONNECTION_SUPPORT = False

    if config.get('manager_address', 0):
        logging.info('entering manager mode')
        manager.run(config)
        return

    tcp_servers = []
    udp_servers = []
    dns_resolver = asyncdns.DNSResolver()
    if int(config['workers']) > 1:
        stat_counter_dict = None
    else:
        stat_counter_dict = {}
    port_password = config['port_password']
    config_password = config.get('password', 'm')
    del config['port_password']
    for port, password_obfs in port_password.items():
        method = config["method"]
        protocol = config.get("protocol", 'origin')
        protocol_param = config.get("protocol_param", '')
        obfs = config.get("obfs", 'plain')
        obfs_param = config.get("obfs_param", '')
        bind = config.get("out_bind", '')
        bindv6 = config.get("out_bindv6", '')
        if type(password_obfs) == list:
            password = password_obfs[0]
            obfs = common.to_str(password_obfs[1])
            if len(password_obfs) > 2:
                protocol = common.to_str(password_obfs[2])
        elif type(password_obfs) == dict:
            password = password_obfs.get('password', config_password)
            method = common.to_str(password_obfs.get('method', method))
            protocol = common.to_str(password_obfs.get('protocol', protocol))
            protocol_param = common.to_str(
                password_obfs.get('protocol_param', protocol_param))
            obfs = common.to_str(password_obfs.get('obfs', obfs))
            obfs_param = common.to_str(
                password_obfs.get('obfs_param', obfs_param))
            bind = password_obfs.get('out_bind', bind)
            bindv6 = password_obfs.get('out_bindv6', bindv6)
        else:
            password = password_obfs
        a_config = config.copy()
        ipv6_ok = False
        logging.info(
            "server start with protocol[%s] password [%s] method [%s] obfs [%s] obfs_param [%s]"
            % (protocol, password, method, obfs, obfs_param))
        if 'server_ipv6' in a_config:
            try:
                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_port'] = int(port)
                a_config['password'] = password
                a_config['method'] = method
                a_config['protocol'] = protocol
                a_config['protocol_param'] = protocol_param
                a_config['obfs'] = obfs
                a_config['obfs_param'] = obfs_param
                a_config['out_bind'] = bind
                a_config['out_bindv6'] = bindv6
                a_config['server'] = a_config['server_ipv6']
                logging.info("starting server at [%s]:%d" %
                             (a_config['server'], int(port)))
                tcp_servers.append(
                    tcprelay.TCPRelay(a_config,
                                      dns_resolver,
                                      False,
                                      stat_counter=stat_counter_dict))
                udp_servers.append(
                    udprelay.UDPRelay(a_config,
                                      dns_resolver,
                                      False,
                                      stat_counter=stat_counter_dict))
                if a_config['server_ipv6'] == b"::":
                    ipv6_ok = True
            except Exception as e:
                shell.print_exception(e)

        try:
            a_config = config.copy()
            a_config['server_port'] = int(port)
            a_config['password'] = password
            a_config['method'] = method
            a_config['protocol'] = protocol
            a_config['protocol_param'] = protocol_param
            a_config['obfs'] = obfs
            a_config['obfs_param'] = obfs_param
            a_config['out_bind'] = bind
            a_config['out_bindv6'] = bindv6
            logging.info("starting server at %s:%d" %
                         (a_config['server'], int(port)))
            tcp_servers.append(
                tcprelay.TCPRelay(a_config,
                                  dns_resolver,
                                  False,
                                  stat_counter=stat_counter_dict))
            udp_servers.append(
                udprelay.UDPRelay(a_config,
                                  dns_resolver,
                                  False,
                                  stat_counter=stat_counter_dict))
        except Exception as e:
            if not ipv6_ok:
                shell.print_exception(e)

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

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

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

        signal.signal(signal.SIGINT, int_handler)

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

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

    if int(config['workers']) > 1:
        if os.name == 'posix':
            children = []
            is_child = False
            for i in range(0, int(config['workers'])):
                r = os.fork()
                if r == 0:
                    logging.info('worker started')
                    is_child = True
                    run_server()
                    break
                else:
                    children.append(r)
            if not is_child:

                def handler(signum, _):
                    for pid in children:
                        try:
                            os.kill(pid, signum)
                            os.waitpid(pid, 0)
                        except OSError:  # child may already exited
                            pass
                    sys.exit()

                signal.signal(signal.SIGTERM, handler)
                signal.signal(signal.SIGQUIT, handler)
                signal.signal(signal.SIGINT, handler)

                # master
                for a_tcp_server in tcp_servers:
                    a_tcp_server.close()
                for a_udp_server in udp_servers:
                    a_udp_server.close()
                dns_resolver.close()

                for child in children:
                    os.waitpid(child, 0)
        else:
            logging.warn('worker is only available on Unix/Linux')
            run_server()
    else:
        run_server()
Example #11
0
def test():
    import time
    import threading
    import struct
    from shadowsocks.core import encrypt

    logging.basicConfig(level=5,
                        format='%(asctime)s %(levelname)-8s %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    enc = []
    eventloop.TIMEOUT_PRECISION = 1

    def run_server():
        shell.parse_args()
        config = shell.parse_config(True)
        config = config.copy()
        a_config = {
            'server': '127.0.0.1',
            'local_port': 1081,
            'port_password': {
                '8381': 'foobar1',
                '8382': 'foobar2'
            },
            'method': 'aes-256-cfb',
            'manager_address': '127.0.0.1:6001',
            'timeout': 60,
            'fast_open': False,
            'verbose': 2
        }
        config.update(a_config)
        manager = Manager(config)
        enc.append(manager)
        manager.run()

    t = threading.Thread(target=run_server)
    t.start()
    time.sleep(1)
    manager = enc[0]
    cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    cli.connect(('127.0.0.1', 6001))

    # test add and remove
    time.sleep(1)
    cli.send(b'add: {"server_port":7001, "password":"******"}')
    time.sleep(1)
    assert 7001 in manager._relays
    data, addr = cli.recvfrom(1506)
    assert b'ok' in data

    cli.send(b'remove: {"server_port":8381}')
    time.sleep(1)
    assert 8381 not in manager._relays
    data, addr = cli.recvfrom(1506)
    assert b'ok' in data
    logger.info('add and remove test passed')

    # test statistics for TCP
    header = common.pack_addr(b'google.com') + struct.pack('>H', 80)
    data = encrypt.encrypt_all(b'asdfadsfasdf', 'aes-256-cfb', 1,
                               header + b'GET /\r\n\r\n')
    tcp_cli = socket.socket()
    tcp_cli.connect(('127.0.0.1', 7001))
    tcp_cli.send(data)
    tcp_cli.recv(4096)
    tcp_cli.close()

    data, addr = cli.recvfrom(1506)
    data = common.to_str(data)
    assert data.startswith('stat: ')
    data = data.split('stat:')[1]
    stats = shell.parse_json_in_str(data)
    assert '7001' in stats
    logger.info('TCP statistics test passed')

    # test statistics for UDP
    header = common.pack_addr(b'127.0.0.1') + struct.pack('>H', 80)
    data = encrypt.encrypt_all(b'foobar2', 'aes-256-cfb', 1, header + b'test')
    udp_cli = socket.socket(type=socket.SOCK_DGRAM)
    udp_cli.sendto(data, ('127.0.0.1', 8382))
    tcp_cli.close()

    data, addr = cli.recvfrom(1506)
    data = common.to_str(data)
    assert data.startswith('stat: ')
    data = data.split('stat:')[1]
    stats = json.loads(data)
    assert '8382' in stats
    logger.info('UDP statistics test passed')

    manager._loop.stop()
    t.join()
Example #12
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)

            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))
            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)
Example #13
0
def base64_encode(string):
    return common.to_str(base64.urlsafe_b64encode(
        common.to_bytes(string))).replace('=', '')
Example #14
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)
Example #15
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