#include "redis.h"
#include <sys/uio.h>
+static void setProtocolError(redisClient *c, int pos);
+
void *dupClientReplyValue(void *o) {
incrRefCount((robj*)o);
return o;
c->reqtype = 0;
c->argc = 0;
c->argv = NULL;
- c->cmd = NULL;
+ c->cmd = c->lastcmd = NULL;
c->multibulklen = 0;
c->bulklen = -1;
c->sentlen = 0;
addReplyBulkCBuffer(c,buf,len);
}
+/* Copy 'src' client output buffers into 'dst' client output buffers.
+ * The function takes care of freeing the old output buffers of the
+ * destination client. */
+void copyClientOutputBuffer(redisClient *dst, redisClient *src) {
+ listRelease(dst->reply);
+ dst->reply = listDup(src->reply);
+ memcpy(dst->buf,src->buf,src->bufpos);
+ dst->bufpos = src->bufpos;
+}
+
static void acceptCommonHandler(int fd) {
redisClient *c;
if ((c = createClient(fd)) == NULL) {
if (write(c->fd,err,strlen(err)) == -1) {
/* Nothing to do, Just to avoid the warning... */
}
+ server.stat_rejected_conn++;
freeClient(c);
return;
}
/* Case 2: we lost the connection with the master. */
if (c->flags & REDIS_MASTER) {
server.master = NULL;
- server.replstate = REDIS_REPL_CONNECT;
+ server.repl_state = REDIS_REPL_CONNECT;
server.repl_down_since = time(NULL);
/* Since we lost the connection with the master, we should also
* close the connection with all our slaves if we have any, so
size_t querylen;
/* Nothing to do without a \r\n */
- if (newline == NULL)
+ if (newline == NULL) {
+ if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {
+ addReplyError(c,"Protocol error: too big inline request");
+ setProtocolError(c,0);
+ }
return REDIS_ERR;
+ }
/* Split the input buffer up to the \r\n */
querylen = newline-(c->querybuf);
/* Helper function. Trims query buffer to make the function that processes
* multi bulk requests idempotent. */
static void setProtocolError(redisClient *c, int pos) {
+ if (server.verbosity >= REDIS_VERBOSE) {
+ sds client = getClientInfoString(c);
+ redisLog(REDIS_VERBOSE,
+ "Protocol error from client: %s", client);
+ sdsfree(client);
+ }
c->flags |= REDIS_CLOSE_AFTER_REPLY;
c->querybuf = sdsrange(c->querybuf,pos,-1);
}
/* Multi bulk length cannot be read without a \r\n */
newline = strchr(c->querybuf,'\r');
- if (newline == NULL)
+ if (newline == NULL) {
+ if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {
+ addReplyError(c,"Protocol error: too big mbulk count string");
+ setProtocolError(c,0);
+ }
return REDIS_ERR;
+ }
/* Buffer should also contain \n */
if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
/* Read bulk length if unknown */
if (c->bulklen == -1) {
newline = strchr(c->querybuf+pos,'\r');
- if (newline == NULL)
+ if (newline == NULL) {
+ if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {
+ addReplyError(c,"Protocol error: too big bulk count string");
+ setProtocolError(c,0);
+ }
break;
+ }
/* Buffer should also contain \n */
if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))
if (pos) c->querybuf = sdsrange(c->querybuf,pos,-1);
/* We're done when c->multibulk == 0 */
- if (c->multibulklen == 0) {
- return REDIS_OK;
- }
+ if (c->multibulklen == 0) return REDIS_OK;
+
+ /* Still not read to process the command */
return REDIS_ERR;
}
} else {
return;
}
+ if (sdslen(c->querybuf) > server.client_max_querybuf_len) {
+ sds ci = getClientInfoString(c), bytes = sdsempty();
+
+ bytes = sdscatrepr(bytes,c->querybuf,64);
+ redisLog(REDIS_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes);
+ sdsfree(ci);
+ sdsfree(bytes);
+ freeClient(c);
+ return;
+ }
processInputBuffer(c);
}
*biggest_input_buffer = bib;
}
-void clientCommand(redisClient *c) {
+/* Turn a Redis client into an sds string representing its state. */
+sds getClientInfoString(redisClient *client) {
+ char ip[32], flags[16], events[3], *p;
+ int port;
+ time_t now = time(NULL);
+ int emask;
+
+ if (anetPeerToString(client->fd,ip,&port) == -1) {
+ ip[0] = '?';
+ ip[1] = '\0';
+ port = 0;
+ }
+ p = flags;
+ if (client->flags & REDIS_SLAVE) {
+ if (client->flags & REDIS_MONITOR)
+ *p++ = 'O';
+ else
+ *p++ = 'S';
+ }
+ if (client->flags & REDIS_MASTER) *p++ = 'M';
+ if (client->flags & REDIS_MULTI) *p++ = 'x';
+ if (client->flags & REDIS_BLOCKED) *p++ = 'b';
+ if (client->flags & REDIS_DIRTY_CAS) *p++ = 'd';
+ if (client->flags & REDIS_CLOSE_AFTER_REPLY) *p++ = 'c';
+ if (client->flags & REDIS_UNBLOCKED) *p++ = 'u';
+ if (p == flags) *p++ = 'N';
+ *p++ = '\0';
+
+ emask = client->fd == -1 ? 0 : aeGetFileEvents(server.el,client->fd);
+ p = events;
+ if (emask & AE_READABLE) *p++ = 'r';
+ if (emask & AE_WRITABLE) *p++ = 'w';
+ *p = '\0';
+ return sdscatprintf(sdsempty(),
+ "addr=%s:%d fd=%d idle=%ld flags=%s db=%d sub=%d psub=%d qbuf=%lu obl=%lu oll=%lu events=%s cmd=%s",
+ ip,port,client->fd,
+ (long)(now - client->lastinteraction),
+ flags,
+ client->db->id,
+ (int) dictSize(client->pubsub_channels),
+ (int) listLength(client->pubsub_patterns),
+ (unsigned long) sdslen(client->querybuf),
+ (unsigned long) client->bufpos,
+ (unsigned long) listLength(client->reply),
+ events,
+ client->lastcmd ? client->lastcmd->name : "NULL");
+}
+
+sds getAllClientsInfoString(void) {
listNode *ln;
listIter li;
redisClient *client;
+ sds o = sdsempty();
- if (!strcasecmp(c->argv[1]->ptr,"list") && c->argc == 2) {
- sds o = sdsempty();
- time_t now = time(NULL);
+ listRewind(server.clients,&li);
+ while ((ln = listNext(&li)) != NULL) {
+ sds cs;
- listRewind(server.clients,&li);
- while ((ln = listNext(&li)) != NULL) {
- char ip[32], flags[16], *p;
- int port;
+ client = listNodeValue(ln);
+ cs = getClientInfoString(client);
+ o = sdscatsds(o,cs);
+ sdsfree(cs);
+ o = sdscatlen(o,"\n",1);
+ }
+ return o;
+}
- client = listNodeValue(ln);
- if (anetPeerToString(client->fd,ip,&port) == -1) continue;
- p = flags;
- if (client->flags & REDIS_SLAVE) {
- if (client->flags & REDIS_MONITOR)
- *p++ = 'O';
- else
- *p++ = 'S';
- }
- if (client->flags & REDIS_MASTER) *p++ = 'M';
- if (p == flags) *p++ = 'N';
- if (client->flags & REDIS_MULTI) *p++ = 'x';
- if (client->flags & REDIS_BLOCKED) *p++ = 'b';
- if (client->flags & REDIS_DIRTY_CAS) *p++ = 'd';
- if (client->flags & REDIS_CLOSE_AFTER_REPLY) *p++ = 'c';
- if (client->flags & REDIS_UNBLOCKED) *p++ = 'u';
- *p++ = '\0';
- o = sdscatprintf(o,
- "addr=%s:%d fd=%d idle=%ld flags=%s db=%d sub=%d psub=%d\n",
- ip,port,client->fd,
- (long)(now - client->lastinteraction),
- flags,
- client->db->id,
- (int) dictSize(client->pubsub_channels),
- (int) listLength(client->pubsub_patterns));
- }
+void clientCommand(redisClient *c) {
+ listNode *ln;
+ listIter li;
+ redisClient *client;
+
+ if (!strcasecmp(c->argv[1]->ptr,"list") && c->argc == 2) {
+ sds o = getAllClientsInfoString();
addReplyBulkCBuffer(c,o,sdslen(o));
sdsfree(o);
} else if (!strcasecmp(c->argv[1]->ptr,"kill") && c->argc == 3) {