diff --git a/src/db.c b/src/db.c index 1ed0bb2af..8d538662e 100644 --- a/src/db.c +++ b/src/db.c @@ -350,6 +350,25 @@ void scanCallback(void *privdata, const dictEntry *de) { if (val) listAddNodeTail(keys, val); } +/* Try to parse a SCAN cursor stored ad object 'o': + * if the cursor is valid, store it as unsigned integer into *cursor and + * returns REDIS_OK. Otherwise return REDIS_ERR and send an error to the + * client. */ +int parseScanCursorOrReply(redisClient *c, robj *o, unsigned long *cursor) { + char *eptr; + + /* Use strtoul() because we need an *unsigned* long, so + * getLongLongFromObject() does not cover the whole cursor space. */ + errno = 0; + *cursor = strtoul(o->ptr, &eptr, 10); + if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' || errno == ERANGE) + { + addReplyError(c, "invalid cursor"); + return REDIS_ERR; + } + return REDIS_OK; +} + /* This command implements SCAN, HSCAN and SSCAN commands. * If object 'o' is passed, then it must be an Hash or Set object, otherwise * if 'o' is NULL the command will operate on the dictionary associated with @@ -361,13 +380,12 @@ void scanCallback(void *privdata, const dictEntry *de) { * * In the case of an Hash object the function returns both the field and value * of every element on the Hash. */ -void scanGenericCommand(redisClient *c, robj *o) { +void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor) { int rv; int i, j; - char buf[REDIS_LONGSTR_SIZE], *eptr; + char buf[REDIS_LONGSTR_SIZE]; list *keys = listCreate(); listNode *node, *nextnode; - unsigned long cursor = 0; long count = 10; sds pat; int patlen, use_pattern = 0; @@ -381,16 +399,6 @@ void scanGenericCommand(redisClient *c, robj *o) { /* Set i to the first option argument. The previous one is the cursor. */ i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */ - /* Use strtoul() because we need an *unsigned* long, so - * getLongLongFromObject() does not cover the whole cursor space. */ - errno = 0; - cursor = strtoul(c->argv[i-1]->ptr, &eptr, 10); - if (isspace(((char*)c->argv[i-1])[0]) || eptr[0] != '\0' || errno == ERANGE) - { - addReplyError(c, "invalid cursor"); - goto cleanup; - } - /* Step 1: Parse options. */ while (i < c->argc) { j = c->argc - i; @@ -548,7 +556,9 @@ cleanup: /* The SCAN command completely relies on scanGenericCommand. */ void scanCommand(redisClient *c) { - scanGenericCommand(c,NULL); + unsigned long cursor; + if (parseScanCursorOrReply(c,c->argv[1],&cursor) == REDIS_ERR) return; + scanGenericCommand(c,NULL,cursor); } void dbsizeCommand(redisClient *c) { diff --git a/src/redis.h b/src/redis.h index 4312b3421..f0b5aa862 100644 --- a/src/redis.h +++ b/src/redis.h @@ -1194,7 +1194,8 @@ void signalFlushedDb(int dbid); unsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count); unsigned int countKeysInSlot(unsigned int hashslot); int verifyClusterConfigWithData(void); -void scanGenericCommand(redisClient *c, robj *o); +void scanGenericCommand(redisClient *c, robj *o, unsigned long cursor); +int parseScanCursorOrReply(redisClient *c, robj *o, unsigned long *cursor); /* API to get key arguments from commands */ #define REDIS_GETKEYS_ALL 0 diff --git a/src/t_hash.c b/src/t_hash.c index 298414f85..f5ceb36e9 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -762,8 +762,10 @@ void hexistsCommand(redisClient *c) { void hscanCommand(redisClient *c) { robj *o; + unsigned long cursor; + if (parseScanCursorOrReply(c,c->argv[2],&cursor) == REDIS_ERR) return; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL || checkType(c,o,REDIS_HASH)) return; - scanGenericCommand(c,o); + scanGenericCommand(c,o,cursor); } diff --git a/src/t_set.c b/src/t_set.c index ebffc5dc0..0ba8335aa 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -909,8 +909,10 @@ void sdiffstoreCommand(redisClient *c) { void sscanCommand(redisClient *c) { robj *set; + unsigned long cursor; + if (parseScanCursorOrReply(c,c->argv[2],&cursor) == REDIS_ERR) return; if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL || checkType(c,set,REDIS_SET)) return; - scanGenericCommand(c,set); + scanGenericCommand(c,set,cursor); } diff --git a/src/t_zset.c b/src/t_zset.c index 1ff289ce1..c3bcbb88f 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -2210,8 +2210,10 @@ void zrevrankCommand(redisClient *c) { void zscanCommand(redisClient *c) { robj *o; + unsigned long cursor; + if (parseScanCursorOrReply(c,c->argv[2],&cursor) == REDIS_ERR) return; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL || checkType(c,o,REDIS_ZSET)) return; - scanGenericCommand(c,o); + scanGenericCommand(c,o,cursor); }