コード例 #1
0
ファイル: mysql_packet.py プロジェクト: yucz/mysql_proxy
def unpack_handshake_packet(buf):
    idx = skip_header()
    handshake = {
        "protocol_version": None,
        "server_version": None,
        "thread_id": None,
        "scramble_1": None,
        "server_capabilities": None,
        "language": None,
        "server_status": None,
        "scramble_2": None,
        "native_password": None,
        "scramble": None
    }

    try:
        handshake["protocol_version"], idx = unpack_int8(buf, idx)
        handshake["server_version"], idx = unpack_string_null(buf, idx)
        if idx == -1:
            sys.exit(0)
        handshake["thread_id"], idx = unpack_int32(buf, idx)
        handshake["scramble_1"], idx = unpack_string(buf, 8, idx)
        idx = skip_packetn(1, idx)
        handshake["server_capabilities"], idx = unpack_int16(buf, idx)
        handshake["language"], idx = unpack_int8(buf, idx)
        handshake["server_status"], idx = unpack_int16(buf, idx)
        idx = skip_packetn(13, idx)
        if handshake[
                "server_capabilities"] & ServerCapability.CLIENT_SECURE_CONNECTION:
            handshake["scramble_2"], idx = unpack_string(buf, 12, idx)
            idx = skip_packetn(1, idx)
        if idx < len(buf):
            handshake["native_password"], idx = unpack_string_null(buf, idx)
    except Exception, msg:
        utils.err(utils.cur(), msg)
コード例 #2
0
ファイル: mysql_packet.py プロジェクト: yucz/mysql_proxy
def unpack_auth_packet(buf):
    """
    analyze authentication packet from client
    """
    idx = skip_header()
    auth = {
        "client_flags": None,
        "max_packet_size": None,
        "charset_number": None,
        "user": None,
        "scramble_buff": None,
        "database": None
    }
    try:
        auth["client_flags"], idx = unpack_int32(buf, idx)
        auth["max_packet_size"], idx = unpack_int32(buf, idx)
        auth["charset_number"], idx = unpack_int8(buf, idx)
        idx = skip_packetn(23, idx)
        auth["user"], idx = unpack_string_null(buf, idx)
        scramble_len, idx = unpack_lenenc(buf, idx)
        auth["scramble_buff"], idx = unpack_string(buf, scramble_len, idx)
        if idx < len(buf):
            auth["database"], idx = unpack_string_null(buf, idx)
    except Exception, err:
        utils.err(utils.cur(), err)
コード例 #3
0
ファイル: listen.py プロジェクト: fifar/mysql_proxy
 def mqCallback(self, channel, method_frame, header_frame, body):
     try:
         if not self.zk.is_proxy_master():
             return
         # master's business
         data_dict = cjson.decode(body)
         # ** MUST ** ack
         channel.basic_ack(method_frame.delivery_tag)
         utils.log(utils.cur(), body, data_dict)
         if not isinstance(data_dict, dict):
             return
         for db, forbid in data_dict.iteritems():
             if not forbid[Forbid.KEY_TYPE] in (Forbid.FORBID_WORKING, Forbid.FORBID_FOREVER):
                 return
             forbid[Forbid.KEY_START] = time.time()
             path = os.path.join(ZKConf.ZK_PATH_FORBID, db)
             orig = self.get_path(path)
             if orig is False:
                 self.zk.mknode(path, cjson.encode(forbid))
             else:
                 old = cjson.decode(orig)
                 if (
                     old[Forbid.KEY_TYPE] == forbid[Forbid.KEY_TYPE]
                     and old[Forbid.KEY_TYPE] == Forbid.FORBID_WORKING
                     and old[Forbid.KEY_START] + old[Forbid.KEY_DURATION] > time.time()
                 ):
                     utils.log(utils.cur(), "still forbidding")
                 else:
                     utils.log(utils.cur(), "change forbid")
                     # change /database/forbid/db
                     self.forbidinfo[db] = forbid
                     self.zk.set(path, cjson.encode(forbid))
     except Exception, err:
         utils.err(utils.cur(), err)
コード例 #4
0
ファイル: ServerProtocol.py プロジェクト: yucz/mysql_proxy
 def _zookeeperAuth(self, auth):
     is_legal = True
     """
     1. check whether client IP is authorized
     """
     try:
         ippath = os.path.join(ZKConf.ZK_PATH_DB, auth["database"], ZKConf.KEY_IP)
         # utils.log(utils.cur(), ippath)
         is_legal = self.factory.get_path(ippath)
         # utils.log(utils.cur(), is_legal)
         if is_legal is not False:
             ip_json = is_legal
             is_legal = True
             # utils.log(utils.cur(), ip_json)
             try:
                 self.ips = ip_helper.IpRangeList(*tuple(cjson.decode(ip_json)))
             except:
                 self.ips = None
             peer = self.transport.getPeer()
             utils.log(utils.cur(), peer, self.ips, self.factory.ips)
             if not ((self.ips and peer.host in self.ips) or 
                     (self.factory.ips and peer.host in self.factory.ips)):
                 is_legal = False
                 ip_error = dict(ErrorCode.IP_RESTRICTED)
                 ip_error["message"] = ip_error["message"] % {"ip":peer.host}
                 self._write(self._goWrong(ip_error, self.next_idx)) # 2
                 return is_legal
     except Exception, err:
         utils.err(utils.cur(), traceback.format_exc())
         is_legal = False
コード例 #5
0
ファイル: listen.py プロジェクト: fifar/mysql_proxy
    def register_watches(self):
        # 1. listen /database/db_info
        #    reset zkdbinfo
        #    close connections which are related to deleted nodes under db_info
        dbs = self.get_path(ZKConf.ZK_PATH_DB, child=True)
        self.zk.watch_child(ZKConf.ZK_PATH_DB, self.watcher_db_info)
        dbs = dbs.keys() if dbs else []
        self.dbs = dbs
        for db in dbs:
            path_authed_ip = os.path.join(ZKConf.ZK_PATH_DB, db, ZKConf.KEY_IP)
            # 2. listen /database/db_info/xxx/authed_ips
            #    reset zkdbinfo
            self.zk.watch_node(path_authed_ip, self.watcher_authed_ip)
            path_dbconf = os.path.join(ZKConf.ZK_PATH_DB, db, ZKConf.KEY_DBCONF)
            # 3. listen /database/db_info/xxx/dbconf/db_host_r
            #    listen /database/db_info/xxx/dbconf/db_host_w
            #    reset zkdbinfo
            #    reset busy_proobj rwclient connections
            #    reset idle_rwclient connections
            self.zk.watch_node(os.path.join(path_dbconf, ZKConf.KEY_READ), self.watcher_rw_ip)
            self.zk.watch_node(os.path.join(path_dbconf, ZKConf.KEY_WRITE), self.watcher_rw_ip)

        # 4. listen /database/authed_ips
        #    reset zkdbinfo
        #    reset self.ips
        ip_json = self.get_path(ZKConf.ZK_PATH_IPS)
        try:
            if ip_json:
                self.ips = ip_helper.IpRangeList(*tuple(cjson.decode(ip_json)))
        except Exception, err:
            utils.err(utils.cur(), err)
            self.ips = None
コード例 #6
0
ファイル: listen.py プロジェクト: yucz/mysql_proxy
    def register_watches(self):
        # 1. listen /database/db_info
        #    reset zkdbinfo
        #    close connections which are related to deleted nodes under db_info
        dbs = self.get_path(ZKConf.ZK_PATH_DB, child=True)
        self.zk.watch_child(ZKConf.ZK_PATH_DB, self.watcher_db_info)
        dbs = dbs.keys() if dbs else []
        self.dbs = dbs
        for db in dbs:
            path_authed_ip = os.path.join(ZKConf.ZK_PATH_DB, db, ZKConf.KEY_IP)
            # 2. listen /database/db_info/xxx/authed_ips
            #    reset zkdbinfo
            self.zk.watch_node(path_authed_ip, self.watcher_authed_ip)
            path_dbconf = os.path.join(ZKConf.ZK_PATH_DB, db, ZKConf.KEY_DBCONF)
            # 3. listen /database/db_info/xxx/dbconf/db_host_r
            #    listen /database/db_info/xxx/dbconf/db_host_w
            #    reset zkdbinfo
            #    reset busy_proobj rwclient connections
            #    reset idle_rwclient connections
            self.zk.watch_node(os.path.join(path_dbconf, ZKConf.KEY_READ), self.watcher_rw_ip)
            self.zk.watch_node(os.path.join(path_dbconf, ZKConf.KEY_WRITE), self.watcher_rw_ip)

        # 4. listen /database/authed_ips
        #    reset zkdbinfo
        #    reset self.ips
        ip_json = self.get_path(ZKConf.ZK_PATH_IPS)
        try:
            if ip_json:
                self.ips = ip_helper.IpRangeList(*tuple(cjson.decode(ip_json)))
        except Exception, err:
            utils.err(utils.cur(), err)
            self.ips = None
コード例 #7
0
ファイル: mysql_packet.py プロジェクト: fifar/mysql_proxy
def unpack_auth_packet(buf):
    """
    analyze authentication packet from client
    """
    idx = skip_header()
    auth = {
        "client_flags" : None,
        "max_packet_size" : None,
        "charset_number" : None,
        "user" : None,
        "scramble_buff" : None,
        "database" : None
    }
    try:
        auth["client_flags"], idx = unpack_int32(buf, idx)
        auth["max_packet_size"], idx = unpack_int32(buf, idx)
        auth["charset_number"], idx = unpack_int8(buf, idx)
        idx = skip_packetn(23, idx)
        auth["user"], idx = unpack_string_null(buf, idx)
        scramble_len, idx = unpack_lenenc(buf, idx)
        auth["scramble_buff"], idx = unpack_string(buf, scramble_len, idx)
        if idx < len(buf):
            auth["database"], idx = unpack_string_null(buf, idx)
    except Exception, err:
        utils.err(utils.cur(), err)
コード例 #8
0
ファイル: listen.py プロジェクト: yucz/mysql_proxy
 def mqCallback(self, channel, method_frame, header_frame, body):
     try:
         if not self.zk.is_proxy_master(): return
         # master's business
         data_dict = cjson.decode(body)
         # ** MUST ** ack
         channel.basic_ack(method_frame.delivery_tag)
         utils.log(utils.cur(), body, data_dict)
         if not isinstance(data_dict, dict):
             return
         for db, forbid in data_dict.iteritems():
             if not forbid[Forbid.KEY_TYPE] in (Forbid.FORBID_WORKING, Forbid.FORBID_FOREVER):
                 return
             forbid[Forbid.KEY_START] = time.time()
             path = os.path.join(ZKConf.ZK_PATH_FORBID, db)
             orig = self.get_path(path)
             if orig is False:
                 self.zk.mknode(path, cjson.encode(forbid))
             else:
                 old = cjson.decode(orig)
                 if old[Forbid.KEY_TYPE] == forbid[Forbid.KEY_TYPE] and \
                         old[Forbid.KEY_TYPE] == Forbid.FORBID_WORKING and \
                         old[Forbid.KEY_START] + old[Forbid.KEY_DURATION] > time.time():
                     utils.log(utils.cur(), "still forbidding")
                 else:
                     utils.log(utils.cur(), "change forbid")
                     # change /database/forbid/db
                     self.forbidinfo[db] = forbid
                     self.zk.set(path, cjson.encode(forbid))
     except Exception, err:
         utils.err(utils.cur(), err)
コード例 #9
0
ファイル: mysql_packet.py プロジェクト: fifar/mysql_proxy
def unpack_handshake_packet(buf):
    idx = skip_header()
    handshake = {
        "protocol_version" : None,
        "server_version"   : None,
        "thread_id"        : None,
        "scramble_1"       : None,
        "server_capabilities" : None,
        "language"            : None,
        "server_status"       : None,
        "scramble_2"          : None,
        "native_password"     : None,
        "scramble"            : None
    }

    try:
        handshake["protocol_version"], idx = unpack_int8(buf, idx)
        handshake["server_version"], idx = unpack_string_null(buf, idx)
        if idx == -1:
            sys.exit(0)
        handshake["thread_id"], idx = unpack_int32(buf, idx)
        handshake["scramble_1"], idx = unpack_string(buf, 8, idx)
        idx = skip_packetn(1, idx)
        handshake["server_capabilities"], idx = unpack_int16(buf, idx)
        handshake["language"], idx = unpack_int8(buf, idx)
        handshake["server_status"], idx = unpack_int16(buf, idx)
        idx = skip_packetn(13, idx)
        if handshake["server_capabilities"] & ServerCapability.CLIENT_SECURE_CONNECTION:
            handshake["scramble_2"], idx = unpack_string(buf, 12, idx)
            idx = skip_packetn(1, idx)
        if idx < len(buf):
            handshake["native_password"], idx = unpack_string_null(buf, idx)
    except Exception, msg:
        utils.err(utils.cur(), msg)
コード例 #10
0
ファイル: listen.py プロジェクト: fifar/mysql_proxy
 def watcher_ip(self, event, true_path):
     self.reload_zkdbinfo()
     ip_json = self.get_path(true_path)
     try:
         self.ips = ip_helper.IpRangeList(*tuple(cjson.decode(ip_json)))
     except Exception, err:
         utils.err(utils.cur(), err)
         self.ips = None
コード例 #11
0
ファイル: listen.py プロジェクト: yucz/mysql_proxy
 def watcher_ip(self, event, true_path):
     self.reload_zkdbinfo()
     ip_json = self.get_path(true_path)
     try:
         self.ips = ip_helper.IpRangeList(*tuple(cjson.decode(ip_json)))
     except Exception, err:
         utils.err(utils.cur(), err)
         self.ips = None
コード例 #12
0
ファイル: mysql_packet.py プロジェクト: fifar/mysql_proxy
def unpack_error_packet(buf):
    idx = skip_header()
    field_count, idx = unpack_int8(buf, idx)
    errno, idx = unpack_int16(buf, idx)  
    sqlstate, idx = unpack_string(buf, 6, idx) # "#state"
    message, idx = unpack_string(buf, len(buf)-idx, idx)
    utils.err(utils.cur(), "%s|%s|%s|%s" % (field_count, errno, sqlstate, message))
    return (field_count, errno, sqlstate, message)
コード例 #13
0
ファイル: mysql_packet.py プロジェクト: yucz/mysql_proxy
def unpack_error_packet(buf):
    idx = skip_header()
    field_count, idx = unpack_int8(buf, idx)
    errno, idx = unpack_int16(buf, idx)
    sqlstate, idx = unpack_string(buf, 6, idx)  # "#state"
    message, idx = unpack_string(buf, len(buf) - idx, idx)
    utils.err(utils.cur(),
              "%s|%s|%s|%s" % (field_count, errno, sqlstate, message))
    return (field_count, errno, sqlstate, message)
コード例 #14
0
ファイル: listen.py プロジェクト: fifar/mysql_proxy
 def watcher_forbid(self, event, true_path):
     self.reload_zkdbinfo()
     dbs_forbid = self.get_path(true_path, child=True)
     forbidinfo = collections.defaultdict(dict)
     for db in dbs_forbid:
         try:
             forbid_db = self.get_path(os.path.join(true_path, db))
             forbidinfo[db] = cjson.decode(forbid_db)
         except Exception, err:
             utils.err(utils.cur(), err)
コード例 #15
0
ファイル: listen.py プロジェクト: yucz/mysql_proxy
 def watcher_forbid(self, event, true_path):
     self.reload_zkdbinfo()
     dbs_forbid = self.get_path(true_path, child=True)
     forbidinfo = collections.defaultdict(dict)
     for db in dbs_forbid:
         try:
             forbid_db = self.get_path(os.path.join(true_path, db))
             forbidinfo[db] = cjson.decode(forbid_db)
         except Exception, err:
             utils.err(utils.cur(), err)
コード例 #16
0
ファイル: dbinfo_znode.py プロジェクト: yucz/mysql_proxy
 def _trans(cur, *args):
     try:
         keys = ",".join(["`%s`" % k for k in dbinfo.keys()])
         vals = ",".join(["'%s'" % v for v in dbinfo.values()])
         sql = "INSERT INTO `%s` (%s) VALUES(%s)" % (
             DBConf.TABLE_DBINFO, keys, vals)
         utils.log(utils.cur(), keys, vals, sql)
         cur.execute(sql)
         cur.execute("COMMIT")
     except Exception, e:
         cur.execute("ROLLBACK")
         utils.err(utils.cur(), e)
         raise Exception(e)
コード例 #17
0
ファイル: dbinfo_znode.py プロジェクト: fifar/mysql_proxy
 def _trans(cur, *args):
     try:
         keys = ",".join(["`%s`" % k for k in dbinfo.keys()])
         vals = ",".join(["'%s'" % v for v in dbinfo.values()])
         sql = "INSERT INTO `%s` (%s) VALUES(%s)" % (DBConf.TABLE_DBINFO,
                                                     keys, 
                                                     vals)
         utils.log(utils.cur(), keys, vals, sql)
         cur.execute(sql)
         cur.execute("COMMIT")
     except Exception, e:
         cur.execute("ROLLBACK")
         utils.err(utils.cur(), e)
         raise Exception(e)
コード例 #18
0
ファイル: mysql_packet.py プロジェクト: fifar/mysql_proxy
def unpack_ok_packet(buf):
    idx = skip_header()
    val, idx = unpack_int8(buf, idx)
    if val is not AuthConf.OK_STATUS: return False
    ok = {
        "affected_rows" : None,
        "insert_id"     : None,
        "server_status" : None,
        "warning_count" : None,
        }
    try:
        ok["affected_rows"], idx = unpack_lenenc(buf, idx)
        ok["insert_id"], idx = unpack_lenenc(buf, idx)
        ok["server_status"], idx = unpack_int16(buf, idx)
        ok["warning_count"], idx = unpack_int16(buf, idx)
    except Exception, err:
        utils.err(utils.cur(), err)
コード例 #19
0
ファイル: mysql_packet.py プロジェクト: yucz/mysql_proxy
def unpack_ok_packet(buf):
    idx = skip_header()
    val, idx = unpack_int8(buf, idx)
    if val is not AuthConf.OK_STATUS: return False
    ok = {
        "affected_rows": None,
        "insert_id": None,
        "server_status": None,
        "warning_count": None,
    }
    try:
        ok["affected_rows"], idx = unpack_lenenc(buf, idx)
        ok["insert_id"], idx = unpack_lenenc(buf, idx)
        ok["server_status"], idx = unpack_int16(buf, idx)
        ok["warning_count"], idx = unpack_int16(buf, idx)
    except Exception, err:
        utils.err(utils.cur(), err)
コード例 #20
0
ファイル: listen.py プロジェクト: yucz/mysql_proxy
class ServerFactory(protocol.Factory):

    def __init__(self, port):
        self.port = port
        self.conns = 0
        self.pool_size = 1
        self.servers = threads.ConnsPool("pool-connections")
        self.packets = None
        self.initPackets(self) # set packets
        self.ips = None # IpRangeList object
        self.dbs = []   # database name
        self.zk = None
        self.zk_dbinfo = {}

        self.forbidinfo = collections.defaultdict(dict)
        self.busy_proobj = collections.defaultdict(list)

        def _init_zk():
            # zk_helper
            self.zk = zk_helper.ZooKeeper("db_proxy", self.register_watches, "register_service", "proxy", self.port)

            self.zk_dbinfo = self.zk.get_dict()
            self.register_watches()

            self.servers.start()

            # mq_helper blocking thread
            self.mq = mq_helper.PikaRecvThread(servers=MQConf.SERVERS,
                                               exchange = MQConf.EXCHANGE_FORBID,
                                               queue = utils.getip() + MQConf.PROXY_SUFFIX,
                                               callback = self.mqCallback)
            self.mq.start()

            self.monitor = threads.ForbidMonitor(self)
            self.monitor.start()

            self.proxy_stats_log = threads.ProxyStatsLog(self.servers)
            self.proxy_stats_log.start()

        reactor.callLater(1, _init_zk)

    def reload_zkdbinfo(self):
        try:
            tmp = dict(self.zk_dbinfo)
            self.zk_dbinfo = self.zk.get_dict()
        except:
            self.zk_dbinfo = dict(tmp)

    def get_path(self, path, child=False):
        db_root_path = ZKConf.ZK_PATH_ROOT
        if path.startswith(db_root_path):
            path = path[len(db_root_path):]
        parts = path.split("/")
        tmp = dict(self.zk_dbinfo)
        for part in parts:
            if not part: continue
            if part in tmp:
                tmp = tmp[part]
            else:
                return False
        if child:
            if "__v__" in tmp: del tmp["__v__"]
            return tmp
        else:
            return tmp["__v__"]

    def register_watches(self):
        # 1. listen /database/db_info
        #    reset zkdbinfo
        #    close connections which are related to deleted nodes under db_info
        dbs = self.get_path(ZKConf.ZK_PATH_DB, child=True)
        self.zk.watch_child(ZKConf.ZK_PATH_DB, self.watcher_db_info)
        dbs = dbs.keys() if dbs else []
        self.dbs = dbs
        for db in dbs:
            path_authed_ip = os.path.join(ZKConf.ZK_PATH_DB, db, ZKConf.KEY_IP)
            # 2. listen /database/db_info/xxx/authed_ips
            #    reset zkdbinfo
            self.zk.watch_node(path_authed_ip, self.watcher_authed_ip)
            path_dbconf = os.path.join(ZKConf.ZK_PATH_DB, db, ZKConf.KEY_DBCONF)
            # 3. listen /database/db_info/xxx/dbconf/db_host_r
            #    listen /database/db_info/xxx/dbconf/db_host_w
            #    reset zkdbinfo
            #    reset busy_proobj rwclient connections
            #    reset idle_rwclient connections
            self.zk.watch_node(os.path.join(path_dbconf, ZKConf.KEY_READ), self.watcher_rw_ip)
            self.zk.watch_node(os.path.join(path_dbconf, ZKConf.KEY_WRITE), self.watcher_rw_ip)

        # 4. listen /database/authed_ips
        #    reset zkdbinfo
        #    reset self.ips
        ip_json = self.get_path(ZKConf.ZK_PATH_IPS)
        try:
            if ip_json:
                self.ips = ip_helper.IpRangeList(*tuple(cjson.decode(ip_json)))
        except Exception, err:
            utils.err(utils.cur(), err)
            self.ips = None
        self.zk.watch_node(ZKConf.ZK_PATH_IPS, self.watcher_ip)

        # 5. listen /database/forbid
        #    reset zkdbinfo
        #    reset self.forbidinfo
        dbs_forbid = self.get_path(ZKConf.ZK_PATH_FORBID, child=True)
        self.zk.watch_child(ZKConf.ZK_PATH_FORBID, self.watcher_forbid)
        dbs_forbid = dbs_forbid.keys() if dbs_forbid else []
        for db in dbs_forbid:
            try:
                data, meta = self.zk.get(os.path.join(ZKConf.ZK_PATH_FORBID, db), None)
                self.forbidinfo[db] = cjson.decode(data)
            except Exception, err:
                utils.err(utils.cur(), err)
コード例 #21
0
ファイル: ServerProtocol.py プロジェクト: yucz/mysql_proxy
class ServerProtocol(protocol.Protocol):

    def __init__(self):
        self.client_authed = False
        self.server = None
        
        self.database = None
        self.dbname = None
        # self.node_listen = True

        # SQL parser
        self.sql_parser = sql_parser.SQLVerify()

        # force read/write
        self.rwsplit = True

        self.timeout = threads.WaitTimeout(self.close_proobj, self)
        self.timeout.start()

        # ip whitelist
        self.ips = None

        self.idx = -1
        self.dbtype = ""

        # prepared statement id
        self.stmt_id = 0

        # requests
        self.requests = 0
        # first.request
        self.req_1st = True
        # receiving trunk
        self.in_trunk = False
        self.buffer = ""
        self.pack_len = 0

    def reset_requests(self):
        self.requests = 0

    @property
    def next_idx(self):
        self.idx += 1
        self.idx %= 256
        return self.idx
    
    def close_proobj(self):
        # utils.log(utils.cur(), "close_proobj")
        self.transport.loseConnection()

    @reset_log
    def connectionLost(self, reason):
        utils.reset_logconf()
        self.factory.conns -= 1
        utils.log(utils.cur(), "client is losing proxy %s" % self.factory.conns, self.requests)
        # mysql_stmt_close
        if self.stmt_id:
            self.server.mysql_stmt_close(self.stmt_id)
        # reclaim connection
        self.factory.takeServer(self.server, self)
        # clean connection
        self.server = None
        self.timeout.stop()

    @change_log
    def connectionMade(self):
        self._write(self.factory.getHandshakeRaw())

    def _checkForbid(self, opts, dbtype):
        if dbtype == ZKConf.INTERNAL or (self.database not in self.factory.forbidinfo):
            return False

        db  = self.database
        msg = self.factory.forbidinfo[db]

        if msg["type"] == Forbid.FORBID_FOREVER:
            return self.sendForbid(msg, db, opts)
        elif msg["type"] == Forbid.FORBID_WORKING:
            start, duration = msg["start"], msg["duration"]
            if start + duration > time.time():
                return self.sendForbid(msg, db, opts)

        self.factory.zk.erase_forbid(db)
        return False

    def sendConnectionTimeout(self):
        timeout_error = dict(ErrorCode.BACKEND_TIMEOUT)
        self._write(self._goWrong(timeout_error, self.next_idx))
        return True

    def sendForbid(self, data, db, opts):
        """
        check contraints of databases, tables and operations
        """
        assert isinstance(data, dict)
        assert "object" in data and data["object"] in (Forbid.FORBID_DATABASE, Forbid.FORBID_TABLE)

        def _realForbid(crud, dbtb, errmsg, isDB=True):
            forbid_duration = int(data["start"] + data["duration"] - time.time()) if data["type"] == Forbid.FORBID_WORKING else sys.maxint
            forbid_error = dict(ErrorCode.QUOTA_EXCEEDED)
            forbid_error["message"] = forbid_error["message"] % (crud, "Database" if isDB else "Table", dbtb, errmsg, forbid_duration)
            self._write(self._goWrong(forbid_error, self.next_idx))
            return True

        crud = data["crud"]
        utils.log(utils.cur(), crud)
        if not crud: return False

        if data["object"] == Forbid.FORBID_DATABASE:
            allopts = opts["db"]
            utils.log(utils.cur(), allopts)
            assert type(allopts) is list
            if Forbid.OPERATION_DEFAULT in crud:
                return _realForbid([Forbid.OPERATION_DEFAULT], db, data["errmsg"], True)
            else:
                if set(allopts).intersection(set(crud)):
                    return _realForbid(crud, db, data["errmsg"], True)
        else:
            allopts = opts["tb"]
            utils.log(utils.cur(), allopts)
            assert type(allopts) is dict            
            for tbl, tbopts in allopts.iteritems():
                if tbl in crud:
                    if Forbid.OPERATION_DEFAULT in crud[tbl]:
                        return _realForbid([Forbid.OPERATION_DEFAULT], tbl, data["errmsg"], False)
                    if set(tbopts).intersection(set(crud[tbl])):
                        return _realForbid(crud[tbl], tbl, data["errmsg"], False)                    

        return False

    @set_idx(1)
    @change_log
    def dataReceived(self, data):
        # client auth first
        if not self.client_authed:
            return self._clientAuth(data)

        # normal sql request, if quit packet return
        if not data or len(data) == 0 or is_quit_packet(data):
            return

        def _r_(request):
            self.requests += 1
            if self.req_1st:
                reactor.callLater(ProxyServer.CONN_TIMEOUT, self.checkFirstReq)
            self._dealQuery(request)

        if self.in_trunk:
            self.buffer += data
            if len(self.buffer) == self.pack_len + 4:
                self.in_trunk = False
                _r_(self.buffer)
        else:
            self.pack_len, _ = unpack_packet_length(data)
            if self.pack_len + 4 > len(data):
                self.in_trunk = True
                self.buffer = data
            elif self.pack_len + 4 == len(data):
                _r_(data)
                        
    def checkFirstReq(self):
        if self.req_1st:
            self.server.close_rw()
            self.sendConnectionTimeout()

    def _clientAuth(self, data):
        """
        1. parse auth packet, db is mandatory
        """
        auth = analyze_packet(data)
        if auth == -1 or not auth["database"]:
            self._write(self._goWrong(ErrorCode.PACKET_WRONG, self.next_idx))
            return

        """
        2. retrieve database info from zookeeper and auth
        """
        is_legal = self._zookeeperAuth(auth)
        utils.log(utils.cur(), "after zkauth", is_legal)
        """
        3. assign a pair of read/write connections when passed
        """
        if is_legal:
            self.database = auth["database"]
            self.dbname = self.getDBname()
            utils.log(utils.cur(), self.database)

            def callback(me, old=False):
                # utils.log(utils.cur(), "auth inside")
                pkts = me.server.writeClient.protocol.packets
                if old:
                    me._write(me._goRight(me.next_idx))
                    me.client_authed = True
                    stats_conns.incr(self.database)
                    utils.log(utils.cur(), stats_conns)
                    return

                if pkts["err"]:
                    me._write(pkts["err"])
                else:
                    """
                    ** NOTICE **
                    be care of index in pkts["ok"] and pkts["err"]
                    """
                    # utils.log(utils.cur(), pkts)
                    p = pkts["ok"][:3] + "\x02" + pkts["ok"][4:]
                    me._write(p)
                    me.client_authed = True
                    stats_conns.incr(self.database)
                    utils.log(utils.cur(), stats_conns)
            
            self.factory.getServer(self.database, callback, self)

    def _goWrong(self, errstate, idx = 1):
        return pack_err_packet(errstate["errno"], errstate["sqlstate"], errstate["message"], idx)

    def pool_error(self, err):
        error = dict(ErrorCode.POOL_ERROR)
        error["message"] = error["message"] % str(err)
        self._write(self._goWrong(error, self.next_idx))

    def service_error(self, host):
        error = dict(ErrorCode.SERVICE_ERROR)
        error["message"] = error["message"] % str(host)
        self._write(self._goWrong(error, self.next_idx))

    def _goRight(self, idx = 1):
        return pack_ok_packet(idx=idx)

    def _zookeeperAuth(self, auth):
        is_legal = True
        """
        1. check whether client IP is authorized
        """
        try:
            ippath = os.path.join(ZKConf.ZK_PATH_DB, auth["database"], ZKConf.KEY_IP)
            # utils.log(utils.cur(), ippath)
            is_legal = self.factory.get_path(ippath)
            # utils.log(utils.cur(), is_legal)
            if is_legal is not False:
                ip_json = is_legal
                is_legal = True
                # utils.log(utils.cur(), ip_json)
                try:
                    self.ips = ip_helper.IpRangeList(*tuple(cjson.decode(ip_json)))
                except:
                    self.ips = None
                peer = self.transport.getPeer()
                utils.log(utils.cur(), peer, self.ips, self.factory.ips)
                if not ((self.ips and peer.host in self.ips) or 
                        (self.factory.ips and peer.host in self.factory.ips)):
                    is_legal = False
                    ip_error = dict(ErrorCode.IP_RESTRICTED)
                    ip_error["message"] = ip_error["message"] % {"ip":peer.host}
                    self._write(self._goWrong(ip_error, self.next_idx)) # 2
                    return is_legal
        except Exception, err:
            utils.err(utils.cur(), traceback.format_exc())
            is_legal = False

        # utils.log(utils.cur(), "ip disabled", is_legal)

        """
        1.1 check whether DB is forbidden
        """
        try:
            if is_legal:
                dis_path = os.path.join(ZKConf.ZK_PATH_DB, auth["database"], ZKConf.KEY_DISABLED)
                is_legal = self.factory.get_path(dis_path)
                if is_legal is not False:
                    disabled = is_legal
                    is_legal = True
                    if int(disabled) == ZKConf.DISABLED:
                        is_legal = False
                        db_error = dict(ErrorCode.DB_DISABLED)
                        db_error["message"] = db_error["message"] % {"db":auth["database"]}
                        self._write(self._goWrong(db_error, self.next_idx))
                        return is_legal
        except Exception, err:
            utils.err(utils.cur(), err)
            is_legal = False
コード例 #22
0
ファイル: ServerProtocol.py プロジェクト: yucz/mysql_proxy
        except Exception, err:
            utils.err(utils.cur(), err)
            is_legal = False

        # utils.log(utils.cur(), "db disabled", is_legal)

        try:
            if is_legal:
                dbtype_path = os.path.join(ZKConf.ZK_PATH_DB, auth["database"], ZKConf.KEY_DBTYPE)
                # utils.log(utils.cur(), dbtype_path)
                is_legal = self.factory.get_path(dbtype_path)
                if is_legal is not False:
                    self.dbtype = is_legal
                    is_legal = True
        except Exception, err:
            utils.err(utils.cur(), traceback.format_exc())
            is_legal = False

        # utils.log(utils.cur(), "db forbidden", is_legal)
        """
        2. find /DB_INFO/database node and retrieve DB_PASS, DB_USER, DB_DB
        """
        try:
            if is_legal:
                dbpath = os.path.join(ZKConf.ZK_PATH_DB, auth["database"], ZKConf.KEY_DBCONF)
                is_legal = self.factory.get_path(dbpath)
                if is_legal is not False:
                    is_legal = True
                    info = {"passwd": "", "user": "", "database":""}
                    info["passwd"]   = self.factory.get_path(dbpath + "/" + ZKConf.KEY_PASSWORD)
                    info["user"]     = self.factory.get_path(dbpath + "/" + ZKConf.KEY_USER)