def auth(self, password): if config.get('requirepass') == config.EMPTY: raise DredisError("client sent AUTH, but no password is set") if password != config.get('requirepass'): self.authenticated = False raise DredisError("invalid password") else: self.authenticated = True
def cmd_zadd(keyspace, key, *args): nx = False xx = False start_index = 0 for i, arg in enumerate(args): if arg.lower() == 'nx': nx = True elif arg.lower() == 'xx': xx = True else: break start_index = i + 1 if nx and xx: raise DredisError( "XX and NX options at the same time are not compatible") args = args[start_index:] if len(args) % 2 != 0: raise DredisSyntaxError() count = 0 pairs = zip(args[0::2], args[1::2]) # [1, 2, 3, 4] -> [(1,2), (3,4)] for score, value in pairs: _validate_zset_score(score) count += keyspace.zadd(key, score, value, nx=nx, xx=xx) return count
def _dump_encoded_string(self, value): if (value >= -(1 << 7)) and (value <= (1 << 7) - 1): return struct.pack('<Bb', (RDB_ENCVAL << 6) | RDB_ENC_INT8, value) elif (value >= -(1 << 15)) and (value <= (1 << 15) - 1): return struct.pack('<Bh', (RDB_ENCVAL << 6) | RDB_ENC_INT16, value) elif (value >= -(1 << 31)) and (value <= (1 << 31) - 1): return struct.pack('<Bi', (RDB_ENCVAL << 6) | RDB_ENC_INT32, value) else: raise DredisError("can't encode %r as integer" % value)
def dump(self, key, key_type): if key_type == 'string': return self.dump_string(key) if key_type == 'set': return self.dump_set(key) if key_type == 'hash': return self.dump_hash(key) if key_type == 'zset': return self.dump_zset(key) raise DredisError("Can't convert %r" % key_type)
def verify_payload(payload): bad_payload = DredisError('DUMP payload version or checksum are wrong') if len(payload) < 10: raise bad_payload data, footer = payload[:-10], payload[-10:] rdb_version, crc = footer[:2], footer[2:] if rdb_version > get_rdb_version(): raise bad_payload if crc64.checksum(data + rdb_version) != crc: raise bad_payload
def load_rdb(self): """ inspired heavily by rdb.c:rdbLoad() """ header = self.file.read(self.REDIS_VERSION_HEADER_LENGTH) if not header.startswith("REDIS"): raise DredisError("Wrong signature trying to load DB from file") version = header[-self.REDIS_VERSION_LENGTH:] if not version.isdigit() or int(version) > RDB_VERSION: raise DredisError("Can't handle RDB format version %s" % version) while True: obj_type = self.load_type() if obj_type == RDB_OPCODE_EXPIRETIME: self.file.read(4) # rdbLoadTime() reads 4 bytes obj_type = self.load_type() logger.warning( "Key expiration isn't supported, skipping expiration (RDB_OPCODE_EXPIRETIME)" ) elif obj_type == RDB_OPCODE_EXPIRETIME_MS: self.file.read(8) # rdbLoadMillisecondTime() reads 8 bytes obj_type = self.load_type() logger.warning( "Key expiration isn't supported, skipping expiration (RDB_OPCODE_EXPIRETIME_MS)" ) elif obj_type == RDB_OPCODE_EOF: break elif obj_type == RDB_OPCODE_SELECTDB: # FIXME: at the moment only add keys to the default db self.load_len() continue elif obj_type == RDB_OPCODE_AUX: # ignoring aux field=value self._load_string() self._load_string() continue elif obj_type == RDB_OPCODE_RESIZEDB: # ignoring db_size & expires_size self.load_len() self.load_len() continue key = self._load_string() self._load(key, obj_type)
def set(option, value): if option in _SERVER_CONFIG: if option == 'debug': value = _validate_bool(option, value) if value == TRUE: logging.getLogger('dredis').setLevel(logging.DEBUG) else: logging.getLogger('dredis').setLevel(logging.INFO) elif option == 'readonly': value = _validate_bool(option, value) _SERVER_CONFIG[option] = value else: raise DredisError('Unsupported CONFIG parameter: {}'.format(option))
def _load_encoded_string(self, enctype): if enctype == RDB_ENC_INT8: result = read_signed_char(self.file) elif enctype == RDB_ENC_INT16: result = read_signed_short(self.file) elif enctype == RDB_ENC_INT32: result = read_signed_int(self.file) elif enctype == RDB_ENC_LZF: compressed_len = self.load_len() out_max_len = self.load_len() data = self.file.read(compressed_len) result = lzf.decompress(data, out_max_len) else: raise DredisError("Unknown RDB string encoding type %d" % enctype) return bytes(result)
def _validate_scan_params(args, cursor): match = None count = 10 args = list(args) try: cursor = int(cursor) except ValueError: raise DredisError("invalid cursor") while args: arg = args.pop(0) if len(args) < 1: raise DredisSyntaxError() else: if arg.lower() == 'match': match = args.pop(0) elif arg.lower() == 'count': try: count = int(args.pop(0)) except ValueError: raise DredisError( "value is not an integer or out of range") else: raise DredisSyntaxError() return cursor, count, match
def run_command(keyspace, cmd, args): logger.debug('[run_command] cmd={}, args={}'.format(repr(cmd), repr(args))) str_args = map(str, args) if cmd.upper() not in REDIS_COMMANDS: raise CommandNotFound("unknown command '{}'".format(cmd)) else: cmd_fn = REDIS_COMMANDS[cmd.upper()] if config.get( 'requirepass' ) != config.EMPTY and not keyspace.authenticated and cmd_fn != cmd_auth: raise AuthenticationRequiredError() if config.get('readonly') == config.TRUE and cmd_fn.flags & CMD_WRITE: raise DredisError("Can't execute %r in readonly mode" % cmd) else: return cmd_fn(keyspace, *str_args)
def cmd_config(keyspace, action, *params): if action.lower() == 'help': # copied from # https://github.com/antirez/redis/blob/cb51bb4320d2240001e8fc4a522d59fb28259703/src/config.c#L2244-L2245 help = [ "GET <pattern> -- Return parameters matching the glob-like <pattern> and their values.", "SET <parameter> <value> -- Set parameter to value.", ] return help elif action.lower() == 'get' and len(params) == 1: return config.get_all(params[0]) elif action.lower() == 'set' and len(params) == 2: config.set(params[0], params[1]) return SimpleString('OK') else: raise DredisError( "Unknown subcommand or wrong number of arguments for '{}'. Try CONFIG HELP." .format(action))
def load_intset(self, key): """ based on intset.h and https://github.com/sripathikrishnan/redis-rdb-tools/blob/543a73e84702e911ddcd31325ecfde77d7fd230b/rdbtools/parser.py#L665-L681 """ # noqa intset = BytesIO(self._load_string()) encoding = read_unsigned_int(intset) length = read_unsigned_int(intset) for _ in xrange(length): if encoding == INTSET_ENC_INT16: entry = read_signed_short(intset) elif encoding == INTSET_ENC_INT32: entry = read_signed_int(intset) elif encoding == INTSET_ENC_INT64: entry = read_signed_long(intset) else: raise DredisError('Invalid encoding %r for intset (key = %r)' % (encoding, key)) self.keyspace.sadd(key, entry)
def _load_ziplist(self, key): """ From ziplist.c: * ZIPLIST OVERALL LAYOUT: * The general layout of the ziplist is as follows: * <zlbytes><zltail><zllen><entry><entry><zlend> """ ziplist = BytesIO(self._load_string()) zlbytes = read_unsigned_int(ziplist) # noqa zltail = read_unsigned_int(ziplist) # noqa zllen = read_unsigned_short(ziplist) for _ in xrange(zllen): yield bytes(self._read_ziplist_entry(ziplist, key)) zlend = read_unsigned_char(ziplist) if zlend != ZIP_END: raise DredisError("Invalid ziplist end %r (key = %r)" % (zlend, key))
def rename(self, old_name, new_name): if self.exists(old_name): if old_name == new_name: return # replace the key that holds the key ID and don't touch the rest key_type = self.type(old_name) if key_type == 'zset': old_db_key = KEY_CODEC.encode_zset(old_name) new_db_key = KEY_CODEC.encode_zset(new_name) elif key_type == 'hash': old_db_key = KEY_CODEC.encode_hash(old_name) new_db_key = KEY_CODEC.encode_hash(new_name) elif key_type == 'set': old_db_key = KEY_CODEC.encode_set(old_name) new_db_key = KEY_CODEC.encode_set(new_name) elif key_type == 'string': old_db_key = KEY_CODEC.encode_string(old_name) new_db_key = KEY_CODEC.encode_string(new_name) else: raise DredisError("invalid key type") self._replace_db_key(new_db_key, old_db_key) else: raise NoKeyError()
def _read_ziplist_entry(self, f, key): """ Copied and adapted from https://github.com/sripathikrishnan/redis-rdb-tools/blob/543a73e84702e911ddcd31325ecfde77d7fd230b/rdbtools/parser.py#L757-L787 """ # noqa length = 0 value = None prev_length = read_unsigned_char(f) if prev_length == ZIP_BIGLEN: prev_length = read_unsigned_int(f) entry_header = read_unsigned_char(f) if (entry_header >> 6) == 0: length = entry_header & 0x3F value = f.read(length) elif (entry_header >> 6) == 1: length = ((entry_header & 0x3F) << 8) | read_unsigned_char(f) value = f.read(length) elif (entry_header >> 6) == 2: length = read_unsigned_int_be(f) value = f.read(length) elif (entry_header >> 4) == 12: value = read_signed_short(f) elif (entry_header >> 4) == 13: value = read_signed_int(f) elif (entry_header >> 4) == 14: value = read_signed_long(f) elif (entry_header == 240): value = read_24bit_signed_number(f) elif (entry_header == 254): value = read_signed_char(f) elif (entry_header >= 241 and entry_header <= 253): value = entry_header - 241 else: raise DredisError('Invalid ziplist entry_header %d for key %s' % (entry_header, key)) return value
def _validate_bool(option, value): if value.lower() not in (TRUE, FALSE): raise DredisError("Invalid argument '{}' for CONFIG SET '{}'".format( value, option)) return value.lower()
RDB_ENCVAL = 3 RDB_ENC_INT8 = 0 RDB_ENC_INT16 = 1 RDB_ENC_INT32 = 2 RDB_ENC_LZF = 3 RDB_OPCODE_AUX = 250 RDB_OPCODE_RESIZEDB = 251 RDB_OPCODE_EXPIRETIME_MS = 252 RDB_OPCODE_EXPIRETIME = 253 RDB_OPCODE_SELECTDB = 254 RDB_OPCODE_EOF = 255 RDB_VERSION = 7 BAD_DATA_FORMAT_ERR = DredisError("Bad data format") def get_rdb_version(): """ :return: little endian encoded 2-byte RDB version """ return struct.pack('<BB', RDB_VERSION & 0xff, (RDB_VERSION >> 8) & 0xff) def load_object(keyspace, key, rdb_file): object_loader = ObjectLoader(keyspace, rdb_file) object_loader.load(key) def generate_payload(keyspace, key):