18 changed files with 1327 additions and 57 deletions
			
			
		@ -0,0 +1,12 @@ | 
				
			|||
#Config | 
				
			|||
MYSQL_HOST = 'mdss.mengsky.net' | 
				
			|||
MYSQL_PORT = 3306 | 
				
			|||
MYSQL_USER = 'ss' | 
				
			|||
MYSQL_PASS = 'ss' | 
				
			|||
MYSQL_DB = 'shadowsocks' | 
				
			|||
 | 
				
			|||
MANAGE_PASS = 'ss233333333' | 
				
			|||
#if you want manage in other server you should set this value to global ip | 
				
			|||
MANAGE_BIND_IP = '127.0.0.1' | 
				
			|||
#make sure this port is idle | 
				
			|||
MANAGE_PORT = 23333 | 
				
			|||
@ -0,0 +1 @@ | 
				
			|||
#!/usr/bin/python | 
				
			|||
@ -0,0 +1,100 @@ | 
				
			|||
#!/usr/bin/env python | 
				
			|||
# -*- coding: utf-8 -*- | 
				
			|||
 | 
				
			|||
# Copyright (c) 2014 clowwindy | 
				
			|||
# | 
				
			|||
# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
# of this software and associated documentation files (the "Software"), to deal | 
				
			|||
# in the Software without restriction, including without limitation the rights | 
				
			|||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
# copies of the Software, and to permit persons to whom the Software is | 
				
			|||
# furnished to do so, subject to the following conditions: | 
				
			|||
# | 
				
			|||
# The above copyright notice and this permission notice shall be included in | 
				
			|||
# all copies or substantial portions of the Software. | 
				
			|||
# | 
				
			|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
# SOFTWARE. | 
				
			|||
 | 
				
			|||
import time | 
				
			|||
import os | 
				
			|||
import socket | 
				
			|||
import struct | 
				
			|||
import re | 
				
			|||
import logging | 
				
			|||
from shadowsocks import common | 
				
			|||
from shadowsocks import lru_cache | 
				
			|||
from shadowsocks import eventloop | 
				
			|||
import server_pool | 
				
			|||
import Config | 
				
			|||
 | 
				
			|||
class ServerMgr(object): | 
				
			|||
 | 
				
			|||
    def __init__(self): | 
				
			|||
        self._loop = None | 
				
			|||
        self._request_id = 1 | 
				
			|||
        self._hosts = {} | 
				
			|||
        self._hostname_status = {} | 
				
			|||
        self._hostname_to_cb = {} | 
				
			|||
        self._cb_to_hostname = {} | 
				
			|||
        self._last_time = time.time() | 
				
			|||
        self._sock = None | 
				
			|||
        self._servers = None | 
				
			|||
 | 
				
			|||
    def add_to_loop(self, loop): | 
				
			|||
        if self._loop: | 
				
			|||
            raise Exception('already add to loop') | 
				
			|||
        self._loop = loop | 
				
			|||
        # TODO when dns server is IPv6 | 
				
			|||
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, | 
				
			|||
                                   socket.SOL_UDP) | 
				
			|||
        self._sock.bind((Config.MANAGE_BIND_IP, Config.MANAGE_PORT)) | 
				
			|||
        self._sock.setblocking(False) | 
				
			|||
        loop.add(self._sock, eventloop.POLL_IN) | 
				
			|||
        loop.add_handler(self.handle_events) | 
				
			|||
 | 
				
			|||
    def _handle_data(self, sock): | 
				
			|||
        data, addr = sock.recvfrom(128) | 
				
			|||
        #manage pwd:port:passwd:action | 
				
			|||
        args = data.split(':') | 
				
			|||
        if len(args) < 4: | 
				
			|||
            return | 
				
			|||
        if args[0] == Config.MANAGE_PASS: | 
				
			|||
            if args[3] == '0': | 
				
			|||
                server_pool.ServerPool.get_instance().cb_del_server(args[1]) | 
				
			|||
            elif args[3] == '1': | 
				
			|||
                server_pool.ServerPool.get_instance().new_server(args[1], args[2]) | 
				
			|||
 | 
				
			|||
    def handle_events(self, events): | 
				
			|||
        for sock, fd, event in events: | 
				
			|||
            if sock != self._sock: | 
				
			|||
                continue | 
				
			|||
            if event & eventloop.POLL_ERR: | 
				
			|||
                logging.error('mgr socket err') | 
				
			|||
                self._loop.remove(self._sock) | 
				
			|||
                self._sock.close() | 
				
			|||
                # TODO when dns server is IPv6 | 
				
			|||
                self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, | 
				
			|||
                                           socket.SOL_UDP) | 
				
			|||
                self._sock.setblocking(False) | 
				
			|||
                self._loop.add(self._sock, eventloop.POLL_IN) | 
				
			|||
            else: | 
				
			|||
                self._handle_data(sock) | 
				
			|||
            break | 
				
			|||
 | 
				
			|||
    def close(self): | 
				
			|||
        if self._sock: | 
				
			|||
            self._sock.close() | 
				
			|||
            self._sock = None | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test(): | 
				
			|||
    pass | 
				
			|||
 | 
				
			|||
if __name__ == '__main__': | 
				
			|||
    test() | 
				
			|||
@ -0,0 +1,12 @@ | 
				
			|||
{ | 
				
			|||
    "server":"0.0.0.0", | 
				
			|||
    "server_ipv6": "::", | 
				
			|||
    "server_port":8388, | 
				
			|||
    "local_address": "127.0.0.1", | 
				
			|||
    "local_port":1080, | 
				
			|||
    "password":"m", | 
				
			|||
    "timeout":300, | 
				
			|||
    "method":"aes-256-cfb", | 
				
			|||
    "fast_open": false, | 
				
			|||
    "workers": 1 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,148 @@ | 
				
			|||
#!/usr/bin/python | 
				
			|||
# -*- coding: UTF-8 -*- | 
				
			|||
 | 
				
			|||
import logging | 
				
			|||
import cymysql | 
				
			|||
import time | 
				
			|||
import sys | 
				
			|||
from server_pool import ServerPool | 
				
			|||
import Config | 
				
			|||
 | 
				
			|||
class DbTransfer(object): | 
				
			|||
 | 
				
			|||
    instance = None | 
				
			|||
 | 
				
			|||
    def __init__(self): | 
				
			|||
        self.last_get_transfer = {} | 
				
			|||
 | 
				
			|||
    @staticmethod | 
				
			|||
    def get_instance(): | 
				
			|||
        if DbTransfer.instance is None: | 
				
			|||
            DbTransfer.instance = DbTransfer() | 
				
			|||
        return DbTransfer.instance | 
				
			|||
 | 
				
			|||
    def push_db_all_user(self): | 
				
			|||
        #更新用户流量到数据库 | 
				
			|||
        last_transfer = self.last_get_transfer | 
				
			|||
        curr_transfer = ServerPool.get_instance().get_servers_transfer() | 
				
			|||
        #上次和本次的增量 | 
				
			|||
        dt_transfer = {} | 
				
			|||
        for id in curr_transfer.keys(): | 
				
			|||
            if id in last_transfer: | 
				
			|||
                if last_transfer[id][0] == curr_transfer[id][0] and last_transfer[id][1] == curr_transfer[id][1]: | 
				
			|||
                    continue | 
				
			|||
                elif curr_transfer[id][0] == 0 and curr_transfer[id][1] == 0: | 
				
			|||
                    continue | 
				
			|||
                elif last_transfer[id][0] <= curr_transfer[id][0] and \ | 
				
			|||
                last_transfer[id][1] <= curr_transfer[id][1]: | 
				
			|||
                    dt_transfer[id] = [curr_transfer[id][0] - last_transfer[id][0], | 
				
			|||
                                       curr_transfer[id][1] - last_transfer[id][1]] | 
				
			|||
                else: | 
				
			|||
                    dt_transfer[id] = [curr_transfer[id][0], curr_transfer[id][1]] | 
				
			|||
            else: | 
				
			|||
                if curr_transfer[id][0] == 0 and curr_transfer[id][1] == 0: | 
				
			|||
                    continue | 
				
			|||
                dt_transfer[id] = [curr_transfer[id][0], curr_transfer[id][1]] | 
				
			|||
 | 
				
			|||
        self.last_get_transfer = curr_transfer | 
				
			|||
        query_head = 'UPDATE user' | 
				
			|||
        query_sub_when = '' | 
				
			|||
        query_sub_when2 = '' | 
				
			|||
        query_sub_in = None | 
				
			|||
        last_time = time.time() | 
				
			|||
        for id in dt_transfer.keys(): | 
				
			|||
            query_sub_when += ' WHEN %s THEN u+%s' % (id, dt_transfer[id][0]) | 
				
			|||
            query_sub_when2 += ' WHEN %s THEN d+%s' % (id, dt_transfer[id][1]) | 
				
			|||
            if query_sub_in is not None: | 
				
			|||
                query_sub_in += ',%s' % id | 
				
			|||
            else: | 
				
			|||
                query_sub_in = '%s' % id | 
				
			|||
        if query_sub_when == '': | 
				
			|||
            return | 
				
			|||
        query_sql = query_head + ' SET u = CASE port' + query_sub_when + \ | 
				
			|||
                    ' END, d = CASE port' + query_sub_when2 + \ | 
				
			|||
                    ' END, t = ' + str(int(last_time)) + \ | 
				
			|||
                    ' WHERE port IN (%s)' % query_sub_in | 
				
			|||
        #print query_sql | 
				
			|||
        conn = cymysql.connect(host=Config.MYSQL_HOST, port=Config.MYSQL_PORT, user=Config.MYSQL_USER, | 
				
			|||
                               passwd=Config.MYSQL_PASS, db=Config.MYSQL_DB, charset='utf8') | 
				
			|||
        cur = conn.cursor() | 
				
			|||
        cur.execute(query_sql) | 
				
			|||
        cur.close() | 
				
			|||
        conn.commit() | 
				
			|||
        conn.close() | 
				
			|||
 | 
				
			|||
    @staticmethod | 
				
			|||
    def pull_db_all_user(): | 
				
			|||
        #数据库所有用户信息 | 
				
			|||
        conn = cymysql.connect(host=Config.MYSQL_HOST, port=Config.MYSQL_PORT, user=Config.MYSQL_USER, | 
				
			|||
                               passwd=Config.MYSQL_PASS, db=Config.MYSQL_DB, charset='utf8') | 
				
			|||
        cur = conn.cursor() | 
				
			|||
        cur.execute("SELECT port, u, d, transfer_enable, passwd, switch, enable, plan FROM user") | 
				
			|||
        rows = [] | 
				
			|||
        for r in cur.fetchall(): | 
				
			|||
            rows.append(list(r)) | 
				
			|||
        cur.close() | 
				
			|||
        conn.close() | 
				
			|||
        return rows | 
				
			|||
 | 
				
			|||
    @staticmethod | 
				
			|||
    def del_server_out_of_bound_safe(last_rows, rows): | 
				
			|||
        #停止超流量的服务 | 
				
			|||
        #启动没超流量的服务 | 
				
			|||
        #需要动态载入switchrule,以便实时修改规则 | 
				
			|||
        cur_servers = {} | 
				
			|||
        for row in rows: | 
				
			|||
            try: | 
				
			|||
                import switchrule | 
				
			|||
                allow = switchrule.isTurnOn(row[7], row[5]) and row[6] == 1 and row[1] + row[2] < row[3] | 
				
			|||
            except Exception, e: | 
				
			|||
                allow = False | 
				
			|||
 | 
				
			|||
            cur_servers[row[0]] = row[4] | 
				
			|||
 | 
				
			|||
            if ServerPool.get_instance().server_is_run(row[0]) > 0: | 
				
			|||
                if not allow: | 
				
			|||
                    logging.info('db stop server at port [%s]' % (row[0])) | 
				
			|||
                    ServerPool.get_instance().del_server(row[0]) | 
				
			|||
                elif (row[0] in ServerPool.get_instance().tcp_servers_pool and ServerPool.get_instance().tcp_servers_pool[row[0]]._config['password'] != row[4]) \ | 
				
			|||
                        or (row[0] in ServerPool.get_instance().tcp_ipv6_servers_pool and ServerPool.get_instance().tcp_ipv6_servers_pool[row[0]]._config['password'] != row[4]): | 
				
			|||
                    #password changed | 
				
			|||
                    logging.info('db stop server at port [%s] reason: password changed' % (row[0])) | 
				
			|||
                    ServerPool.get_instance().del_server(row[0]) | 
				
			|||
            elif ServerPool.get_instance().server_run_status(row[0]) is False: | 
				
			|||
                if allow: | 
				
			|||
                    logging.info('db start server at port [%s] pass [%s]' % (row[0], row[4])) | 
				
			|||
                    ServerPool.get_instance().new_server(row[0], row[4]) | 
				
			|||
 | 
				
			|||
        for row in last_rows: | 
				
			|||
            if row[0] in cur_servers: | 
				
			|||
                if row[4] == cur_servers[row[0]]: | 
				
			|||
                    pass | 
				
			|||
            else: | 
				
			|||
                logging.info('db stop server at port [%s] reason: port not exist' % (row[0])) | 
				
			|||
                ServerPool.get_instance().del_server(row[0]) | 
				
			|||
 | 
				
			|||
    @staticmethod | 
				
			|||
    def thread_db(): | 
				
			|||
        import socket | 
				
			|||
        import time | 
				
			|||
        timeout = 60 | 
				
			|||
        socket.setdefaulttimeout(timeout) | 
				
			|||
        last_rows = [] | 
				
			|||
        while True: | 
				
			|||
            #logging.warn('db loop') | 
				
			|||
 | 
				
			|||
            try: | 
				
			|||
                DbTransfer.get_instance().push_db_all_user() | 
				
			|||
                rows = DbTransfer.get_instance().pull_db_all_user() | 
				
			|||
                DbTransfer.del_server_out_of_bound_safe(last_rows, rows) | 
				
			|||
                last_rows = rows | 
				
			|||
            except Exception as e: | 
				
			|||
                logging.warn('db thread except:%s' % e) | 
				
			|||
            finally: | 
				
			|||
                time.sleep(15) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
#SQLData.pull_db_all_user() | 
				
			|||
#print DbTransfer.get_instance().test() | 
				
			|||
@ -0,0 +1,22 @@ | 
				
			|||
#!/usr/bin/env python | 
				
			|||
# -*- coding: utf-8 -*- | 
				
			|||
 | 
				
			|||
import time | 
				
			|||
import sys | 
				
			|||
import thread | 
				
			|||
import os | 
				
			|||
os.chdir(os.path.split(os.path.realpath(__file__))[0]) | 
				
			|||
 | 
				
			|||
import server_pool | 
				
			|||
import db_transfer | 
				
			|||
 | 
				
			|||
#def test(): | 
				
			|||
#    thread.start_new_thread(DbTransfer.thread_db, ()) | 
				
			|||
#    Api.web_server() | 
				
			|||
 | 
				
			|||
if __name__ == '__main__': | 
				
			|||
    #server_pool.ServerPool.get_instance() | 
				
			|||
    #server_pool.ServerPool.get_instance().new_server(2333, '2333') | 
				
			|||
    thread.start_new_thread(db_transfer.DbTransfer.thread_db, ()) | 
				
			|||
    while True: | 
				
			|||
        time.sleep(99999) | 
				
			|||
@ -0,0 +1,211 @@ | 
				
			|||
#!/usr/bin/env python | 
				
			|||
# -*- coding: utf-8 -*- | 
				
			|||
 | 
				
			|||
# Copyright (c) 2014 clowwindy | 
				
			|||
# | 
				
			|||
# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
# of this software and associated documentation files (the "Software"), to deal | 
				
			|||
# in the Software without restriction, including without limitation the rights | 
				
			|||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
# copies of the Software, and to permit persons to whom the Software is | 
				
			|||
# furnished to do so, subject to the following conditions: | 
				
			|||
# | 
				
			|||
# The above copyright notice and this permission notice shall be included in | 
				
			|||
# all copies or substantial portions of the Software. | 
				
			|||
# | 
				
			|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
# SOFTWARE. | 
				
			|||
 | 
				
			|||
import os | 
				
			|||
import logging | 
				
			|||
import time | 
				
			|||
from shadowsocks import utils | 
				
			|||
from shadowsocks import eventloop | 
				
			|||
from shadowsocks import tcprelay | 
				
			|||
from shadowsocks import udprelay | 
				
			|||
from shadowsocks import asyncdns | 
				
			|||
import thread | 
				
			|||
import threading | 
				
			|||
import sys | 
				
			|||
import asyncmgr | 
				
			|||
import Config | 
				
			|||
from socket import * | 
				
			|||
 | 
				
			|||
class ServerPool(object): | 
				
			|||
 | 
				
			|||
    instance = None | 
				
			|||
 | 
				
			|||
    def __init__(self): | 
				
			|||
        utils.check_python() | 
				
			|||
        self.config = utils.get_config(False) | 
				
			|||
        utils.print_shadowsocks() | 
				
			|||
        self.dns_resolver = asyncdns.DNSResolver() | 
				
			|||
        self.mgr = asyncmgr.ServerMgr() | 
				
			|||
        self.udp_on = True ### UDP switch ===================================== | 
				
			|||
 | 
				
			|||
        self.tcp_servers_pool = {} | 
				
			|||
        self.tcp_ipv6_servers_pool = {} | 
				
			|||
        self.udp_servers_pool = {} | 
				
			|||
        self.udp_ipv6_servers_pool = {} | 
				
			|||
 | 
				
			|||
        self.loop = eventloop.EventLoop() | 
				
			|||
        thread.start_new_thread(ServerPool._loop, (self.loop, self.dns_resolver, self.mgr)) | 
				
			|||
 | 
				
			|||
    @staticmethod | 
				
			|||
    def get_instance(): | 
				
			|||
        if ServerPool.instance is None: | 
				
			|||
            ServerPool.instance = ServerPool() | 
				
			|||
        return ServerPool.instance | 
				
			|||
 | 
				
			|||
    @staticmethod | 
				
			|||
    def _loop(loop, dns_resolver, mgr): | 
				
			|||
        try: | 
				
			|||
            mgr.add_to_loop(loop) | 
				
			|||
            dns_resolver.add_to_loop(loop) | 
				
			|||
            loop.run() | 
				
			|||
        except (KeyboardInterrupt, IOError, OSError) as e: | 
				
			|||
            logging.error(e) | 
				
			|||
            import traceback | 
				
			|||
            traceback.print_exc() | 
				
			|||
            os.exit(0) | 
				
			|||
 | 
				
			|||
    def server_is_run(self, port): | 
				
			|||
        port = int(port) | 
				
			|||
        ret = 0 | 
				
			|||
        if port in self.tcp_servers_pool: | 
				
			|||
            ret = 1 | 
				
			|||
        if port in self.tcp_ipv6_servers_pool: | 
				
			|||
            ret |= 2 | 
				
			|||
        return ret | 
				
			|||
 | 
				
			|||
    def server_run_status(self, port): | 
				
			|||
        if 'server' in self.config: | 
				
			|||
            if port not in self.tcp_servers_pool: | 
				
			|||
                return False | 
				
			|||
        if 'server_ipv6' in self.config: | 
				
			|||
            if port not in self.tcp_ipv6_servers_pool: | 
				
			|||
                return False | 
				
			|||
        return True | 
				
			|||
 | 
				
			|||
    def new_server(self, port, password): | 
				
			|||
        ret = True | 
				
			|||
        port = int(port) | 
				
			|||
 | 
				
			|||
        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['server'] = a_config['server_ipv6'] | 
				
			|||
                a_config['server_port'] = port | 
				
			|||
                a_config['password'] = password | 
				
			|||
                try: | 
				
			|||
                    logging.info("starting server at %s:%d" % (a_config['server'], port)) | 
				
			|||
                    tcp_server = tcprelay.TCPRelay(a_config, self.dns_resolver, False) | 
				
			|||
                    tcp_server.add_to_loop(self.loop) | 
				
			|||
                    self.tcp_ipv6_servers_pool.update({port: tcp_server}) | 
				
			|||
                    if self.udp_on: | 
				
			|||
                        udp_server = udprelay.UDPRelay(a_config, self.dns_resolver, False) | 
				
			|||
                        udp_server.add_to_loop(self.loop) | 
				
			|||
                        self.udp_ipv6_servers_pool.update({port: udp_server}) | 
				
			|||
                except Exception, e: | 
				
			|||
                    logging.warn("IPV6 exception") | 
				
			|||
                    logging.warn(e) | 
				
			|||
 | 
				
			|||
        if 'server' in self.config: | 
				
			|||
            if port in self.tcp_servers_pool: | 
				
			|||
                logging.info("server already at %s:%d" % (self.config['server'], port)) | 
				
			|||
                return 'this port server is already running' | 
				
			|||
            else: | 
				
			|||
                a_config = self.config.copy() | 
				
			|||
                a_config['server_port'] = port | 
				
			|||
                a_config['password'] = password | 
				
			|||
                try: | 
				
			|||
                    logging.info("starting server at %s:%d" % (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}) | 
				
			|||
                    if self.udp_on: | 
				
			|||
                        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, e: | 
				
			|||
                    logging.warn("IPV4 exception") | 
				
			|||
                    logging.warn(e) | 
				
			|||
 | 
				
			|||
        return True | 
				
			|||
 | 
				
			|||
    def del_server(self, port): | 
				
			|||
        port = int(port) | 
				
			|||
        logging.info("del server at %d" % port) | 
				
			|||
        try: | 
				
			|||
            udpsock = socket(AF_INET, SOCK_DGRAM) | 
				
			|||
            udpsock.sendto('%s:%s:0:0' % (Config.MANAGE_PASS, port), (Config.MANAGE_BIND_IP, Config.MANAGE_PORT)) | 
				
			|||
            udpsock.close() | 
				
			|||
        except Exception, e: | 
				
			|||
            logging.warn(e) | 
				
			|||
        return True | 
				
			|||
 | 
				
			|||
    def cb_del_server(self, port): | 
				
			|||
        port = int(port) | 
				
			|||
 | 
				
			|||
        if port not in self.tcp_servers_pool: | 
				
			|||
            logging.info("stopped server at %s:%d already stop" % (self.config['server'], port)) | 
				
			|||
        else: | 
				
			|||
            logging.info("stopped server at %s:%d" % (self.config['server'], port)) | 
				
			|||
            try: | 
				
			|||
                self.tcp_servers_pool[port].destroy() | 
				
			|||
                del self.tcp_servers_pool[port] | 
				
			|||
            except Exception, e: | 
				
			|||
                logging.warn(e) | 
				
			|||
            if self.udp_on: | 
				
			|||
                try: | 
				
			|||
                    self.udp_servers_pool[port].destroy() | 
				
			|||
                    del self.udp_servers_pool[port] | 
				
			|||
                except Exception, e: | 
				
			|||
                    logging.warn(e) | 
				
			|||
 | 
				
			|||
        if 'server_ipv6' in self.config: | 
				
			|||
            if port not in self.tcp_ipv6_servers_pool: | 
				
			|||
                logging.info("stopped server at %s:%d already stop" % (self.config['server_ipv6'], port)) | 
				
			|||
            else: | 
				
			|||
                logging.info("stopped server at %s:%d" % (self.config['server_ipv6'], port)) | 
				
			|||
                try: | 
				
			|||
                    self.tcp_ipv6_servers_pool[port].destroy() | 
				
			|||
                    del self.tcp_ipv6_servers_pool[port] | 
				
			|||
                except Exception, e: | 
				
			|||
                    logging.warn(e) | 
				
			|||
                if self.udp_on: | 
				
			|||
                    try: | 
				
			|||
                        self.udp_ipv6_servers_pool[port].destroy() | 
				
			|||
                        del self.udp_ipv6_servers_pool[port] | 
				
			|||
                    except Exception, e: | 
				
			|||
                        logging.warn(e) | 
				
			|||
 | 
				
			|||
            return True | 
				
			|||
 | 
				
			|||
    def get_server_transfer(self, port): | 
				
			|||
        port = int(port) | 
				
			|||
        ret = [0, 0] | 
				
			|||
        if port in self.tcp_servers_pool: | 
				
			|||
            ret[0] = self.tcp_servers_pool[port].server_transfer_ul | 
				
			|||
            ret[1] = self.tcp_servers_pool[port].server_transfer_dl | 
				
			|||
        if port in self.tcp_ipv6_servers_pool: | 
				
			|||
            ret[0] += self.tcp_ipv6_servers_pool[port].server_transfer_ul | 
				
			|||
            ret[1] += self.tcp_ipv6_servers_pool[port].server_transfer_dl | 
				
			|||
        return ret | 
				
			|||
 | 
				
			|||
    def get_servers_transfer(self): | 
				
			|||
        servers = self.tcp_servers_pool.copy() | 
				
			|||
        servers.update(self.tcp_ipv6_servers_pool) | 
				
			|||
        ret = {} | 
				
			|||
        for port in servers.keys(): | 
				
			|||
            ret[port] = self.get_server_transfer(port) | 
				
			|||
        return ret | 
				
			|||
 | 
				
			|||
@ -1,39 +0,0 @@ | 
				
			|||
import codecs | 
				
			|||
from setuptools import setup | 
				
			|||
 | 
				
			|||
 | 
				
			|||
with codecs.open('README.rst', encoding='utf-8') as f: | 
				
			|||
    long_description = f.read() | 
				
			|||
 | 
				
			|||
setup( | 
				
			|||
    name="shadowsocks", | 
				
			|||
    version="2.6.11", | 
				
			|||
    license='http://www.apache.org/licenses/LICENSE-2.0', | 
				
			|||
    description="A fast tunnel proxy that help you get through firewalls", | 
				
			|||
    author='clowwindy', | 
				
			|||
    author_email='clowwindy42@gmail.com', | 
				
			|||
    url='https://github.com/shadowsocks/shadowsocks', | 
				
			|||
    packages=['shadowsocks', 'shadowsocks.crypto'], | 
				
			|||
    package_data={ | 
				
			|||
        'shadowsocks': ['README.rst', 'LICENSE'] | 
				
			|||
    }, | 
				
			|||
    install_requires=[], | 
				
			|||
    entry_points=""" | 
				
			|||
    [console_scripts] | 
				
			|||
    sslocal = shadowsocks.local:main | 
				
			|||
    ssserver = shadowsocks.server:main | 
				
			|||
    """, | 
				
			|||
    classifiers=[ | 
				
			|||
        'License :: OSI Approved :: Apache Software License', | 
				
			|||
        'Programming Language :: Python :: 2', | 
				
			|||
        'Programming Language :: Python :: 2.6', | 
				
			|||
        'Programming Language :: Python :: 2.7', | 
				
			|||
        'Programming Language :: Python :: 3', | 
				
			|||
        'Programming Language :: Python :: 3.3', | 
				
			|||
        'Programming Language :: Python :: 3.4', | 
				
			|||
        'Programming Language :: Python :: Implementation :: CPython', | 
				
			|||
        'Programming Language :: Python :: Implementation :: PyPy', | 
				
			|||
        'Topic :: Internet :: Proxy Servers', | 
				
			|||
    ], | 
				
			|||
    long_description=long_description, | 
				
			|||
) | 
				
			|||
@ -0,0 +1,24 @@ | 
				
			|||
SET FOREIGN_KEY_CHECKS=0; | 
				
			|||
 | 
				
			|||
CREATE TABLE `user` ( | 
				
			|||
  `id` int(11) NOT NULL AUTO_INCREMENT, | 
				
			|||
  `email` varchar(32) NOT NULL, | 
				
			|||
  `pass` varchar(16) NOT NULL, | 
				
			|||
  `passwd` varchar(16) NOT NULL, | 
				
			|||
  `t` int(11) NOT NULL DEFAULT '0', | 
				
			|||
  `u` bigint(20) NOT NULL, | 
				
			|||
  `d` bigint(20) NOT NULL, | 
				
			|||
  `transfer_enable` bigint(20) NOT NULL, | 
				
			|||
  `port` int(11) NOT NULL, | 
				
			|||
  `switch` tinyint(4) NOT NULL DEFAULT '1', | 
				
			|||
  `enable` tinyint(4) NOT NULL DEFAULT '1', | 
				
			|||
  `type` tinyint(4) NOT NULL DEFAULT '1', | 
				
			|||
  `last_get_gift_time` int(11) NOT NULL DEFAULT '0', | 
				
			|||
  `last_rest_pass_time` int(11) NOT NULL DEFAULT '0', | 
				
			|||
  PRIMARY KEY (`id`,`port`) | 
				
			|||
) ENGINE=InnoDB AUTO_INCREMENT=415 DEFAULT CHARSET=utf8; | 
				
			|||
 | 
				
			|||
-- ---------------------------- | 
				
			|||
-- Records of user | 
				
			|||
-- ---------------------------- | 
				
			|||
INSERT INTO `user` VALUES ('7', 'test@test.com', '123456', '0000000', '1410609560', '0', '0', '9320666234', '50000', '1', '1', '7', '0', '0'); | 
				
			|||
@ -0,0 +1,135 @@ | 
				
			|||
#!/usr/bin/env python | 
				
			|||
 | 
				
			|||
# Copyright (c) 2014 clowwindy | 
				
			|||
# | 
				
			|||
# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
# of this software and associated documentation files (the "Software"), to deal | 
				
			|||
# in the Software without restriction, including without limitation the rights | 
				
			|||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
# copies of the Software, and to permit persons to whom the Software is | 
				
			|||
# furnished to do so, subject to the following conditions: | 
				
			|||
# | 
				
			|||
# The above copyright notice and this permission notice shall be included in | 
				
			|||
# all copies or substantial portions of the Software. | 
				
			|||
# | 
				
			|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
# SOFTWARE. | 
				
			|||
 | 
				
			|||
from __future__ import absolute_import, division, print_function, \ | 
				
			|||
    with_statement | 
				
			|||
 | 
				
			|||
import logging | 
				
			|||
from ctypes import CDLL, c_char_p, c_int, c_ulonglong, byref, \ | 
				
			|||
    create_string_buffer, c_void_p | 
				
			|||
 | 
				
			|||
__all__ = ['ciphers'] | 
				
			|||
 | 
				
			|||
libsodium = None | 
				
			|||
loaded = False | 
				
			|||
 | 
				
			|||
buf_size = 2048 | 
				
			|||
 | 
				
			|||
# for salsa20 and chacha20 | 
				
			|||
BLOCK_SIZE = 64 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def load_libsodium(): | 
				
			|||
    global loaded, libsodium, buf | 
				
			|||
 | 
				
			|||
    from ctypes.util import find_library | 
				
			|||
    for p in ('sodium',): | 
				
			|||
        libsodium_path = find_library(p) | 
				
			|||
        if libsodium_path: | 
				
			|||
            break | 
				
			|||
    else: | 
				
			|||
        raise Exception('libsodium not found') | 
				
			|||
    logging.info('loading libsodium from %s', libsodium_path) | 
				
			|||
    libsodium = CDLL(libsodium_path) | 
				
			|||
    libsodium.sodium_init.restype = c_int | 
				
			|||
    libsodium.crypto_stream_salsa20_xor_ic.restype = c_int | 
				
			|||
    libsodium.crypto_stream_salsa20_xor_ic.argtypes = (c_void_p, c_char_p, | 
				
			|||
                                                       c_ulonglong, | 
				
			|||
                                                       c_char_p, c_ulonglong, | 
				
			|||
                                                       c_char_p) | 
				
			|||
    libsodium.crypto_stream_chacha20_xor_ic.restype = c_int | 
				
			|||
    libsodium.crypto_stream_chacha20_xor_ic.argtypes = (c_void_p, c_char_p, | 
				
			|||
                                                        c_ulonglong, | 
				
			|||
                                                        c_char_p, c_ulonglong, | 
				
			|||
                                                        c_char_p) | 
				
			|||
 | 
				
			|||
    libsodium.sodium_init() | 
				
			|||
 | 
				
			|||
    buf = create_string_buffer(buf_size) | 
				
			|||
    loaded = True | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class Salsa20Crypto(object): | 
				
			|||
    def __init__(self, cipher_name, key, iv, op): | 
				
			|||
        if not loaded: | 
				
			|||
            load_libsodium() | 
				
			|||
        self.key = key | 
				
			|||
        self.iv = iv | 
				
			|||
        self.key_ptr = c_char_p(key) | 
				
			|||
        self.iv_ptr = c_char_p(iv) | 
				
			|||
        if cipher_name == b'salsa20': | 
				
			|||
            self.cipher = libsodium.crypto_stream_salsa20_xor_ic | 
				
			|||
        elif cipher_name == b'chacha20': | 
				
			|||
            self.cipher = libsodium.crypto_stream_chacha20_xor_ic | 
				
			|||
        else: | 
				
			|||
            raise Exception('Unknown cipher') | 
				
			|||
        # byte counter, not block counter | 
				
			|||
        self.counter = 0 | 
				
			|||
 | 
				
			|||
    def update(self, data): | 
				
			|||
        global buf_size, buf | 
				
			|||
        l = len(data) | 
				
			|||
 | 
				
			|||
        # we can only prepend some padding to make the encryption align to | 
				
			|||
        # blocks | 
				
			|||
        padding = self.counter % BLOCK_SIZE | 
				
			|||
        if buf_size < padding + l: | 
				
			|||
            buf_size = (padding + l) * 2 | 
				
			|||
            buf = create_string_buffer(buf_size) | 
				
			|||
 | 
				
			|||
        if padding: | 
				
			|||
            data = (b'\0' * padding) + data | 
				
			|||
        self.cipher(byref(buf), c_char_p(data), padding + l, | 
				
			|||
                    self.iv_ptr, int(self.counter / BLOCK_SIZE), self.key_ptr) | 
				
			|||
        self.counter += l | 
				
			|||
        # buf is copied to a str object when we access buf.raw | 
				
			|||
        # strip off the padding | 
				
			|||
        return buf.raw[padding:padding + l] | 
				
			|||
 | 
				
			|||
 | 
				
			|||
ciphers = { | 
				
			|||
    b'salsa20': (32, 8, Salsa20Crypto), | 
				
			|||
    b'chacha20': (32, 8, Salsa20Crypto), | 
				
			|||
} | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_salsa20(): | 
				
			|||
    from shadowsocks.crypto import util | 
				
			|||
 | 
				
			|||
    cipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 1) | 
				
			|||
    decipher = Salsa20Crypto(b'salsa20', b'k' * 32, b'i' * 16, 0) | 
				
			|||
 | 
				
			|||
    util.run_cipher(cipher, decipher) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_chacha20(): | 
				
			|||
    from shadowsocks.crypto import util | 
				
			|||
 | 
				
			|||
    cipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 1) | 
				
			|||
    decipher = Salsa20Crypto(b'chacha20', b'k' * 32, b'i' * 16, 0) | 
				
			|||
 | 
				
			|||
    util.run_cipher(cipher, decipher) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
if __name__ == '__main__': | 
				
			|||
    test_chacha20() | 
				
			|||
    test_salsa20() | 
				
			|||
@ -0,0 +1,188 @@ | 
				
			|||
#!/usr/bin/env python | 
				
			|||
 | 
				
			|||
# Copyright (c) 2014 clowwindy | 
				
			|||
# | 
				
			|||
# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
# of this software and associated documentation files (the "Software"), to deal | 
				
			|||
# in the Software without restriction, including without limitation the rights | 
				
			|||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
# copies of the Software, and to permit persons to whom the Software is | 
				
			|||
# furnished to do so, subject to the following conditions: | 
				
			|||
# | 
				
			|||
# The above copyright notice and this permission notice shall be included in | 
				
			|||
# all copies or substantial portions of the Software. | 
				
			|||
# | 
				
			|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
# SOFTWARE. | 
				
			|||
 | 
				
			|||
from __future__ import absolute_import, division, print_function, \ | 
				
			|||
    with_statement | 
				
			|||
 | 
				
			|||
import logging | 
				
			|||
from ctypes import CDLL, c_char_p, c_int, c_long, byref,\ | 
				
			|||
    create_string_buffer, c_void_p | 
				
			|||
 | 
				
			|||
__all__ = ['ciphers'] | 
				
			|||
 | 
				
			|||
libcrypto = None | 
				
			|||
loaded = False | 
				
			|||
 | 
				
			|||
buf_size = 2048 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def load_openssl(): | 
				
			|||
    global loaded, libcrypto, buf | 
				
			|||
 | 
				
			|||
    from ctypes.util import find_library | 
				
			|||
    for p in ('crypto', 'eay32', 'libeay32'): | 
				
			|||
        libcrypto_path = find_library(p) | 
				
			|||
        if libcrypto_path: | 
				
			|||
            break | 
				
			|||
    else: | 
				
			|||
        raise Exception('libcrypto(OpenSSL) not found') | 
				
			|||
    logging.info('loading libcrypto from %s', libcrypto_path) | 
				
			|||
    libcrypto = CDLL(libcrypto_path) | 
				
			|||
    libcrypto.EVP_get_cipherbyname.restype = c_void_p | 
				
			|||
    libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p | 
				
			|||
 | 
				
			|||
    libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_char_p, | 
				
			|||
                                            c_char_p, c_char_p, c_int) | 
				
			|||
 | 
				
			|||
    libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_void_p, c_void_p, | 
				
			|||
                                           c_char_p, c_int) | 
				
			|||
 | 
				
			|||
    libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,) | 
				
			|||
    libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p,) | 
				
			|||
    if hasattr(libcrypto, 'OpenSSL_add_all_ciphers'): | 
				
			|||
        libcrypto.OpenSSL_add_all_ciphers() | 
				
			|||
 | 
				
			|||
    buf = create_string_buffer(buf_size) | 
				
			|||
    loaded = True | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def load_cipher(cipher_name): | 
				
			|||
    func_name = b'EVP_' + cipher_name.replace(b'-', b'_') | 
				
			|||
    if bytes != str: | 
				
			|||
        func_name = str(func_name, 'utf-8') | 
				
			|||
    cipher = getattr(libcrypto, func_name, None) | 
				
			|||
    if cipher: | 
				
			|||
        cipher.restype = c_void_p | 
				
			|||
        return cipher() | 
				
			|||
    return None | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class CtypesCrypto(object): | 
				
			|||
    def __init__(self, cipher_name, key, iv, op): | 
				
			|||
        if not loaded: | 
				
			|||
            load_openssl() | 
				
			|||
        self._ctx = None | 
				
			|||
        cipher = libcrypto.EVP_get_cipherbyname(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') | 
				
			|||
 | 
				
			|||
    def update(self, data): | 
				
			|||
        global buf_size, buf | 
				
			|||
        cipher_out_len = c_long(0) | 
				
			|||
        l = len(data) | 
				
			|||
        if buf_size < l: | 
				
			|||
            buf_size = l * 2 | 
				
			|||
            buf = create_string_buffer(buf_size) | 
				
			|||
        libcrypto.EVP_CipherUpdate(self._ctx, byref(buf), | 
				
			|||
                                   byref(cipher_out_len), c_char_p(data), l) | 
				
			|||
        # buf is copied to a str object when we access buf.raw | 
				
			|||
        return buf.raw[:cipher_out_len.value] | 
				
			|||
 | 
				
			|||
    def __del__(self): | 
				
			|||
        self.clean() | 
				
			|||
 | 
				
			|||
    def clean(self): | 
				
			|||
        if self._ctx: | 
				
			|||
            libcrypto.EVP_CIPHER_CTX_cleanup(self._ctx) | 
				
			|||
            libcrypto.EVP_CIPHER_CTX_free(self._ctx) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
ciphers = { | 
				
			|||
    b'aes-128-cfb': (16, 16, CtypesCrypto), | 
				
			|||
    b'aes-192-cfb': (24, 16, CtypesCrypto), | 
				
			|||
    b'aes-256-cfb': (32, 16, CtypesCrypto), | 
				
			|||
    b'aes-128-ofb': (16, 16, CtypesCrypto), | 
				
			|||
    b'aes-192-ofb': (24, 16, CtypesCrypto), | 
				
			|||
    b'aes-256-ofb': (32, 16, CtypesCrypto), | 
				
			|||
    b'aes-128-ctr': (16, 16, CtypesCrypto), | 
				
			|||
    b'aes-192-ctr': (24, 16, CtypesCrypto), | 
				
			|||
    b'aes-256-ctr': (32, 16, CtypesCrypto), | 
				
			|||
    b'aes-128-cfb8': (16, 16, CtypesCrypto), | 
				
			|||
    b'aes-192-cfb8': (24, 16, CtypesCrypto), | 
				
			|||
    b'aes-256-cfb8': (32, 16, CtypesCrypto), | 
				
			|||
    b'aes-128-cfb1': (16, 16, CtypesCrypto), | 
				
			|||
    b'aes-192-cfb1': (24, 16, CtypesCrypto), | 
				
			|||
    b'aes-256-cfb1': (32, 16, CtypesCrypto), | 
				
			|||
    b'bf-cfb': (16, 8, CtypesCrypto), | 
				
			|||
    b'camellia-128-cfb': (16, 16, CtypesCrypto), | 
				
			|||
    b'camellia-192-cfb': (24, 16, CtypesCrypto), | 
				
			|||
    b'camellia-256-cfb': (32, 16, CtypesCrypto), | 
				
			|||
    b'cast5-cfb': (16, 8, CtypesCrypto), | 
				
			|||
    b'des-cfb': (8, 8, CtypesCrypto), | 
				
			|||
    b'idea-cfb': (16, 8, CtypesCrypto), | 
				
			|||
    b'rc2-cfb': (16, 8, CtypesCrypto), | 
				
			|||
    b'rc4': (16, 0, CtypesCrypto), | 
				
			|||
    b'seed-cfb': (16, 16, CtypesCrypto), | 
				
			|||
} | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def run_method(method): | 
				
			|||
    from shadowsocks.crypto import util | 
				
			|||
 | 
				
			|||
    cipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 1) | 
				
			|||
    decipher = CtypesCrypto(method, b'k' * 32, b'i' * 16, 0) | 
				
			|||
 | 
				
			|||
    util.run_cipher(cipher, decipher) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_aes_128_cfb(): | 
				
			|||
    run_method(b'aes-128-cfb') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_aes_256_cfb(): | 
				
			|||
    run_method(b'aes-256-cfb') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_aes_128_cfb8(): | 
				
			|||
    run_method(b'aes-128-cfb8') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_aes_256_ofb(): | 
				
			|||
    run_method(b'aes-256-ofb') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_aes_256_ctr(): | 
				
			|||
    run_method(b'aes-256-ctr') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_bf_cfb(): | 
				
			|||
    run_method(b'bf-cfb') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_rc4(): | 
				
			|||
    run_method(b'rc4') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
if __name__ == '__main__': | 
				
			|||
    test_aes_128_cfb() | 
				
			|||
@ -0,0 +1,117 @@ | 
				
			|||
#!/usr/bin/env python | 
				
			|||
 | 
				
			|||
# Copyright (c) 2014 clowwindy | 
				
			|||
# | 
				
			|||
# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
# of this software and associated documentation files (the "Software"), to deal | 
				
			|||
# in the Software without restriction, including without limitation the rights | 
				
			|||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
# copies of the Software, and to permit persons to whom the Software is | 
				
			|||
# furnished to do so, subject to the following conditions: | 
				
			|||
# | 
				
			|||
# The above copyright notice and this permission notice shall be included in | 
				
			|||
# all copies or substantial portions of the Software. | 
				
			|||
# | 
				
			|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
# SOFTWARE. | 
				
			|||
 | 
				
			|||
from __future__ import absolute_import, division, print_function, \ | 
				
			|||
    with_statement | 
				
			|||
 | 
				
			|||
import sys | 
				
			|||
import logging | 
				
			|||
 | 
				
			|||
__all__ = ['ciphers'] | 
				
			|||
 | 
				
			|||
has_m2 = True | 
				
			|||
try: | 
				
			|||
    __import__('M2Crypto') | 
				
			|||
except ImportError: | 
				
			|||
    has_m2 = False | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def create_cipher(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, i=1, | 
				
			|||
                  padding=1): | 
				
			|||
 | 
				
			|||
    import M2Crypto.EVP | 
				
			|||
    return M2Crypto.EVP.Cipher(alg.replace('-', '_'), key, iv, op, | 
				
			|||
                               key_as_bytes=0, d='md5', salt=None, i=1, | 
				
			|||
                               padding=1) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def err(alg, key, iv, op, key_as_bytes=0, d=None, salt=None, i=1, padding=1): | 
				
			|||
    logging.error(('M2Crypto is required to use %s, please run' | 
				
			|||
                   ' `apt-get install python-m2crypto`') % alg) | 
				
			|||
    sys.exit(1) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
if has_m2: | 
				
			|||
    ciphers = { | 
				
			|||
        b'aes-128-cfb': (16, 16, create_cipher), | 
				
			|||
        b'aes-192-cfb': (24, 16, create_cipher), | 
				
			|||
        b'aes-256-cfb': (32, 16, create_cipher), | 
				
			|||
        b'bf-cfb': (16, 8, create_cipher), | 
				
			|||
        b'camellia-128-cfb': (16, 16, create_cipher), | 
				
			|||
        b'camellia-192-cfb': (24, 16, create_cipher), | 
				
			|||
        b'camellia-256-cfb': (32, 16, create_cipher), | 
				
			|||
        b'cast5-cfb': (16, 8, create_cipher), | 
				
			|||
        b'des-cfb': (8, 8, create_cipher), | 
				
			|||
        b'idea-cfb': (16, 8, create_cipher), | 
				
			|||
        b'rc2-cfb': (16, 8, create_cipher), | 
				
			|||
        b'rc4': (16, 0, create_cipher), | 
				
			|||
        b'seed-cfb': (16, 16, create_cipher), | 
				
			|||
    } | 
				
			|||
else: | 
				
			|||
    ciphers = {} | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def run_method(method): | 
				
			|||
    from shadowsocks.crypto import util | 
				
			|||
 | 
				
			|||
    cipher = create_cipher(method, b'k' * 32, b'i' * 16, 1) | 
				
			|||
    decipher = create_cipher(method, b'k' * 32, b'i' * 16, 0) | 
				
			|||
 | 
				
			|||
    util.run_cipher(cipher, decipher) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def check_env(): | 
				
			|||
    # skip this test on pypy and Python 3 | 
				
			|||
    try: | 
				
			|||
        import __pypy__ | 
				
			|||
        del __pypy__ | 
				
			|||
        from nose.plugins.skip import SkipTest | 
				
			|||
        raise SkipTest | 
				
			|||
    except ImportError: | 
				
			|||
        pass | 
				
			|||
    if bytes != str: | 
				
			|||
        from nose.plugins.skip import SkipTest | 
				
			|||
        raise SkipTest | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_aes_128_cfb(): | 
				
			|||
    check_env() | 
				
			|||
    run_method(b'aes-128-cfb') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_aes_256_cfb(): | 
				
			|||
    check_env() | 
				
			|||
    run_method(b'aes-256-cfb') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_bf_cfb(): | 
				
			|||
    check_env() | 
				
			|||
    run_method(b'bf-cfb') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def test_rc4(): | 
				
			|||
    check_env() | 
				
			|||
    run_method(b'rc4') | 
				
			|||
 | 
				
			|||
 | 
				
			|||
if __name__ == '__main__': | 
				
			|||
    test_aes_128_cfb() | 
				
			|||
@ -0,0 +1,278 @@ | 
				
			|||
#!/usr/bin/python | 
				
			|||
# -*- coding: utf-8 -*- | 
				
			|||
 | 
				
			|||
# Copyright (c) 2014 clowwindy | 
				
			|||
# | 
				
			|||
# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
# of this software and associated documentation files (the "Software"), to deal | 
				
			|||
# in the Software without restriction, including without limitation the rights | 
				
			|||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
# copies of the Software, and to permit persons to whom the Software is | 
				
			|||
# furnished to do so, subject to the following conditions: | 
				
			|||
# | 
				
			|||
# The above copyright notice and this permission notice shall be included in | 
				
			|||
# all copies or substantial portions of the Software. | 
				
			|||
# | 
				
			|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
# SOFTWARE. | 
				
			|||
 | 
				
			|||
import os | 
				
			|||
import json | 
				
			|||
import sys | 
				
			|||
import getopt | 
				
			|||
import logging | 
				
			|||
 | 
				
			|||
 | 
				
			|||
VERBOSE_LEVEL = 5 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def check_python(): | 
				
			|||
    info = sys.version_info | 
				
			|||
    if not (info[0] == 2 and info[1] >= 6): | 
				
			|||
        print 'Python 2.6 or 2.7 required' | 
				
			|||
        sys.exit(1) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def print_shadowsocks(): | 
				
			|||
    version = '' | 
				
			|||
    try: | 
				
			|||
        import pkg_resources | 
				
			|||
        version = pkg_resources.get_distribution('shadowsocks').version | 
				
			|||
    except Exception: | 
				
			|||
        pass | 
				
			|||
    print 'shadowsocks %s' % version | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def find_config(): | 
				
			|||
    config_path = 'config.json' | 
				
			|||
    if os.path.exists(config_path): | 
				
			|||
        return config_path | 
				
			|||
    config_path = os.path.join(os.path.dirname(__file__), '../', 'config.json') | 
				
			|||
    if os.path.exists(config_path): | 
				
			|||
        return config_path | 
				
			|||
    return None | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def check_config(config): | 
				
			|||
    if config.get('local_address', '') in ['0.0.0.0']: | 
				
			|||
        logging.warn('warning: local set to listen 0.0.0.0, which is not safe') | 
				
			|||
    if config.get('server', '') in ['127.0.0.1', 'localhost']: | 
				
			|||
        logging.warn('warning: server set to listen %s:%s, are you sure?' % | 
				
			|||
                     (config['server'], config['server_port'])) | 
				
			|||
    if (config.get('method', '') or '').lower() == '': | 
				
			|||
        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 ['mypassword', 'barfoo!']: | 
				
			|||
        logging.error('DON\'T USE DEFAULT PASSWORD! Please change it in your ' | 
				
			|||
                      'config.json!') | 
				
			|||
        exit(1) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_config(is_local): | 
				
			|||
    logging.basicConfig(level=logging.INFO, | 
				
			|||
                        format='%(levelname)-s: %(message)s', filemode='a+') | 
				
			|||
    if is_local: | 
				
			|||
        shortopts = 'hs:b:p:k:l:m:c:t:vq' | 
				
			|||
        longopts = ['fast-open'] | 
				
			|||
    else: | 
				
			|||
        shortopts = 'hs:p:k:m:c:t:vq' | 
				
			|||
        longopts = ['fast-open', 'workers:'] | 
				
			|||
    try: | 
				
			|||
        config_path = find_config() | 
				
			|||
        optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) | 
				
			|||
        for key, value in optlist: | 
				
			|||
            if key == '-c': | 
				
			|||
                config_path = value | 
				
			|||
 | 
				
			|||
        if config_path: | 
				
			|||
            logging.info('loading config from %s' % config_path) | 
				
			|||
            with open(config_path, 'rb') as f: | 
				
			|||
                try: | 
				
			|||
                    config = json.load(f, object_hook=_decode_dict) | 
				
			|||
                except ValueError as e: | 
				
			|||
                    logging.error('found an error in config.json: %s', | 
				
			|||
                                  e.message) | 
				
			|||
                    sys.exit(1) | 
				
			|||
        else: | 
				
			|||
            config = {} | 
				
			|||
 | 
				
			|||
        optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) | 
				
			|||
        v_count = 0 | 
				
			|||
        for key, value in optlist: | 
				
			|||
            if key == '-p': | 
				
			|||
                config['server_port'] = int(value) | 
				
			|||
            elif key == '-k': | 
				
			|||
                config['password'] = value | 
				
			|||
            elif key == '-l': | 
				
			|||
                config['local_port'] = int(value) | 
				
			|||
            elif key == '-s': | 
				
			|||
                config['server'] = value | 
				
			|||
            elif key == '-m': | 
				
			|||
                config['method'] = value | 
				
			|||
            elif key == '-b': | 
				
			|||
                config['local_address'] = value | 
				
			|||
            elif key == '-v': | 
				
			|||
                v_count += 1 | 
				
			|||
                # '-vv' turns on more verbose mode | 
				
			|||
                config['verbose'] = v_count | 
				
			|||
            elif key == '-t': | 
				
			|||
                config['timeout'] = int(value) | 
				
			|||
            elif key == '--fast-open': | 
				
			|||
                config['fast_open'] = True | 
				
			|||
            elif key == '--workers': | 
				
			|||
                config['workers'] = value | 
				
			|||
            elif key == '-h': | 
				
			|||
                if is_local: | 
				
			|||
                    print_local_help() | 
				
			|||
                else: | 
				
			|||
                    print_server_help() | 
				
			|||
                sys.exit(0) | 
				
			|||
            elif key == '-q': | 
				
			|||
                v_count -= 1 | 
				
			|||
                config['verbose'] = v_count | 
				
			|||
    except getopt.GetoptError as e: | 
				
			|||
        print >>sys.stderr, e | 
				
			|||
        print_help(is_local) | 
				
			|||
        sys.exit(2) | 
				
			|||
 | 
				
			|||
    if not config: | 
				
			|||
        logging.error('config not specified') | 
				
			|||
        print_help(is_local) | 
				
			|||
        sys.exit(2) | 
				
			|||
 | 
				
			|||
    config['password'] = config.get('password', None) | 
				
			|||
    config['method'] = config.get('method', 'aes-256-cfb') | 
				
			|||
    config['port_password'] = config.get('port_password', None) | 
				
			|||
    config['timeout'] = int(config.get('timeout', 300)) | 
				
			|||
    config['fast_open'] = config.get('fast_open', False) | 
				
			|||
    config['workers'] = config.get('workers', 1) | 
				
			|||
    config['verbose'] = config.get('verbose', False) | 
				
			|||
    config['local_address'] = config.get('local_address', '127.0.0.1') | 
				
			|||
    config['local_port'] = config.get('local_port', 1080) | 
				
			|||
    if is_local: | 
				
			|||
        if config.get('server', None) is None: | 
				
			|||
            logging.error('server addr not specified') | 
				
			|||
            print_local_help() | 
				
			|||
            sys.exit(2) | 
				
			|||
    else: | 
				
			|||
        config['server'] = config.get('server', '0.0.0.0') | 
				
			|||
    config['server_port'] = config.get('server_port', 8388) | 
				
			|||
 | 
				
			|||
    if not ('password' in config and config['password']): | 
				
			|||
        logging.error('password not specified') | 
				
			|||
        print_help(is_local) | 
				
			|||
        sys.exit(2) | 
				
			|||
 | 
				
			|||
    logging.getLogger('').handlers = [] | 
				
			|||
    logging.addLevelName(VERBOSE_LEVEL, 'VERBOSE') | 
				
			|||
    if config['verbose'] >= 2: | 
				
			|||
        level = VERBOSE_LEVEL | 
				
			|||
    elif config['verbose'] == 1: | 
				
			|||
        level = logging.DEBUG | 
				
			|||
    elif config['verbose'] == -1: | 
				
			|||
        level = logging.WARN | 
				
			|||
    elif config['verbose'] <= -2: | 
				
			|||
        level = logging.ERROR | 
				
			|||
    else: | 
				
			|||
        level = logging.INFO | 
				
			|||
    logging.basicConfig(level=level, | 
				
			|||
                        format='%(asctime)s %(levelname)-8s %(message)s', | 
				
			|||
                        datefmt='%Y-%m-%d %H:%M:%S', filemode='a+') | 
				
			|||
 | 
				
			|||
    check_config(config) | 
				
			|||
 | 
				
			|||
    return config | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def print_help(is_local): | 
				
			|||
    if is_local: | 
				
			|||
        print_local_help() | 
				
			|||
    else: | 
				
			|||
        print_server_help() | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def print_local_help(): | 
				
			|||
    print '''usage: sslocal [-h] -s SERVER_ADDR [-p SERVER_PORT] | 
				
			|||
               [-b LOCAL_ADDR] [-l LOCAL_PORT] -k PASSWORD [-m METHOD] | 
				
			|||
               [-t TIMEOUT] [-c CONFIG] [--fast-open] [-v] [-q] | 
				
			|||
 | 
				
			|||
optional arguments: | 
				
			|||
  -h, --help            show this help message and exit | 
				
			|||
  -s SERVER_ADDR        server address | 
				
			|||
  -p SERVER_PORT        server port, default: 8388 | 
				
			|||
  -b LOCAL_ADDR         local binding address, default: 127.0.0.1 | 
				
			|||
  -l LOCAL_PORT         local port, default: 1080 | 
				
			|||
  -k PASSWORD           password | 
				
			|||
  -m METHOD             encryption method, default: aes-256-cfb | 
				
			|||
  -t TIMEOUT            timeout in seconds, default: 300 | 
				
			|||
  -c CONFIG             path to config file | 
				
			|||
  --fast-open           use TCP_FASTOPEN, requires Linux 3.7+ | 
				
			|||
  -v, -vv               verbose mode | 
				
			|||
  -q, -qq               quiet mode, only show warnings/errors | 
				
			|||
 | 
				
			|||
Online help: <https://github.com/clowwindy/shadowsocks> | 
				
			|||
''' | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def print_server_help(): | 
				
			|||
    print '''usage: ssserver [-h] [-s SERVER_ADDR] [-p SERVER_PORT] -k PASSWORD | 
				
			|||
                -m METHOD [-t TIMEOUT] [-c CONFIG] [--fast-open] | 
				
			|||
                [--workers WORKERS] [-v] [-q] | 
				
			|||
 | 
				
			|||
optional arguments: | 
				
			|||
  -h, --help            show this help message and exit | 
				
			|||
  -s SERVER_ADDR        server address, default: 0.0.0.0 | 
				
			|||
  -p SERVER_PORT        server port, default: 8388 | 
				
			|||
  -k PASSWORD           password | 
				
			|||
  -m METHOD             encryption method, default: aes-256-cfb | 
				
			|||
  -t TIMEOUT            timeout in seconds, default: 300 | 
				
			|||
  -c CONFIG             path to config file | 
				
			|||
  --fast-open           use TCP_FASTOPEN, requires Linux 3.7+ | 
				
			|||
  --workers WORKERS     number of workers, available on Unix/Linux | 
				
			|||
  -v, -vv               verbose mode | 
				
			|||
  -q, -qq               quiet mode, only show warnings/errors | 
				
			|||
 | 
				
			|||
Online help: <https://github.com/clowwindy/shadowsocks> | 
				
			|||
''' | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def _decode_list(data): | 
				
			|||
    rv = [] | 
				
			|||
    for item in data: | 
				
			|||
        if isinstance(item, unicode): | 
				
			|||
            item = item.encode('utf-8') | 
				
			|||
        elif isinstance(item, list): | 
				
			|||
            item = _decode_list(item) | 
				
			|||
        elif isinstance(item, dict): | 
				
			|||
            item = _decode_dict(item) | 
				
			|||
        rv.append(item) | 
				
			|||
    return rv | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def _decode_dict(data): | 
				
			|||
    rv = {} | 
				
			|||
    for key, value in data.iteritems(): | 
				
			|||
        if isinstance(key, unicode): | 
				
			|||
            key = key.encode('utf-8') | 
				
			|||
        if isinstance(value, unicode): | 
				
			|||
            value = value.encode('utf-8') | 
				
			|||
        elif isinstance(value, list): | 
				
			|||
            value = _decode_list(value) | 
				
			|||
        elif isinstance(value, dict): | 
				
			|||
            value = _decode_dict(value) | 
				
			|||
        rv[key] = value | 
				
			|||
    return rv | 
				
			|||
@ -0,0 +1,2 @@ | 
				
			|||
def isTurnOn(plan, switch): | 
				
			|||
	return True | 
				
			|||
					Loading…
					
					
				
		Reference in new issue