1 /*
2 * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3 * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4 * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
5 * Jan-Erik Rediger <janerik at fnordig dot com>
6 *
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * * Neither the name of Redis nor the names of its contributors may be used
18 * to endorse or promote products derived from this software without
19 * specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "fmacros.h"
35 #include <string.h>
36 #include <stdlib.h>
37 #include <assert.h>
38 #include <errno.h>
39 #include <ctype.h>
40
41 #include "hiredis.h"
42 #include "net.h"
43 #include "sds.h"
44 #include "async.h"
45 #include "win32.h"
46
47 extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout);
48 extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
49
50 static redisContextFuncs redisContextDefaultFuncs = {
51 .free_privctx = NULL,
52 .async_read = redisAsyncRead,
53 .async_write = redisAsyncWrite,
54 .read = redisNetRead,
55 .write = redisNetWrite
56 };
57
58 static redisReply *createReplyObject(int type);
59 static void *createStringObject(const redisReadTask *task, char *str, size_t len);
60 static void *createArrayObject(const redisReadTask *task, size_t elements);
61 static void *createIntegerObject(const redisReadTask *task, long long value);
62 static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);
63 static void *createNilObject(const redisReadTask *task);
64 static void *createBoolObject(const redisReadTask *task, int bval);
65
66 /* Default set of functions to build the reply. Keep in mind that such a
67 * function returning NULL is interpreted as OOM. */
68 static redisReplyObjectFunctions defaultFunctions = {
69 createStringObject,
70 createArrayObject,
71 createIntegerObject,
72 createDoubleObject,
73 createNilObject,
74 createBoolObject,
75 freeReplyObject
76 };
77
78 /* Create a reply object */
createReplyObject(int type)79 static redisReply *createReplyObject(int type) {
80 redisReply *r = hi_calloc(1,sizeof(*r));
81
82 if (r == NULL)
83 return NULL;
84
85 r->type = type;
86 return r;
87 }
88
89 /* Free a reply object */
freeReplyObject(void * reply)90 void freeReplyObject(void *reply) {
91 redisReply *r = reply;
92 size_t j;
93
94 if (r == NULL)
95 return;
96
97 switch(r->type) {
98 case REDIS_REPLY_INTEGER:
99 break; /* Nothing to free */
100 case REDIS_REPLY_ARRAY:
101 case REDIS_REPLY_MAP:
102 case REDIS_REPLY_SET:
103 case REDIS_REPLY_PUSH:
104 if (r->element != NULL) {
105 for (j = 0; j < r->elements; j++)
106 freeReplyObject(r->element[j]);
107 hi_free(r->element);
108 }
109 break;
110 case REDIS_REPLY_ERROR:
111 case REDIS_REPLY_STATUS:
112 case REDIS_REPLY_STRING:
113 case REDIS_REPLY_DOUBLE:
114 case REDIS_REPLY_VERB:
115 hi_free(r->str);
116 break;
117 }
118 hi_free(r);
119 }
120
createStringObject(const redisReadTask * task,char * str,size_t len)121 static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
122 redisReply *r, *parent;
123 char *buf;
124
125 r = createReplyObject(task->type);
126 if (r == NULL)
127 return NULL;
128
129 assert(task->type == REDIS_REPLY_ERROR ||
130 task->type == REDIS_REPLY_STATUS ||
131 task->type == REDIS_REPLY_STRING ||
132 task->type == REDIS_REPLY_VERB);
133
134 /* Copy string value */
135 if (task->type == REDIS_REPLY_VERB) {
136 buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
137 if (buf == NULL) goto oom;
138
139 memcpy(r->vtype,str,3);
140 r->vtype[3] = '\0';
141 memcpy(buf,str+4,len-4);
142 buf[len-4] = '\0';
143 r->len = len - 4;
144 } else {
145 buf = hi_malloc(len+1);
146 if (buf == NULL) goto oom;
147
148 memcpy(buf,str,len);
149 buf[len] = '\0';
150 r->len = len;
151 }
152 r->str = buf;
153
154 if (task->parent) {
155 parent = task->parent->obj;
156 assert(parent->type == REDIS_REPLY_ARRAY ||
157 parent->type == REDIS_REPLY_MAP ||
158 parent->type == REDIS_REPLY_SET ||
159 parent->type == REDIS_REPLY_PUSH);
160 parent->element[task->idx] = r;
161 }
162 return r;
163
164 oom:
165 freeReplyObject(r);
166 return NULL;
167 }
168
createArrayObject(const redisReadTask * task,size_t elements)169 static void *createArrayObject(const redisReadTask *task, size_t elements) {
170 redisReply *r, *parent;
171
172 r = createReplyObject(task->type);
173 if (r == NULL)
174 return NULL;
175
176 if (elements > 0) {
177 r->element = hi_calloc(elements,sizeof(redisReply*));
178 if (r->element == NULL) {
179 freeReplyObject(r);
180 return NULL;
181 }
182 }
183
184 r->elements = elements;
185
186 if (task->parent) {
187 parent = task->parent->obj;
188 assert(parent->type == REDIS_REPLY_ARRAY ||
189 parent->type == REDIS_REPLY_MAP ||
190 parent->type == REDIS_REPLY_SET ||
191 parent->type == REDIS_REPLY_PUSH);
192 parent->element[task->idx] = r;
193 }
194 return r;
195 }
196
createIntegerObject(const redisReadTask * task,long long value)197 static void *createIntegerObject(const redisReadTask *task, long long value) {
198 redisReply *r, *parent;
199
200 r = createReplyObject(REDIS_REPLY_INTEGER);
201 if (r == NULL)
202 return NULL;
203
204 r->integer = value;
205
206 if (task->parent) {
207 parent = task->parent->obj;
208 assert(parent->type == REDIS_REPLY_ARRAY ||
209 parent->type == REDIS_REPLY_MAP ||
210 parent->type == REDIS_REPLY_SET ||
211 parent->type == REDIS_REPLY_PUSH);
212 parent->element[task->idx] = r;
213 }
214 return r;
215 }
216
createDoubleObject(const redisReadTask * task,double value,char * str,size_t len)217 static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {
218 redisReply *r, *parent;
219
220 r = createReplyObject(REDIS_REPLY_DOUBLE);
221 if (r == NULL)
222 return NULL;
223
224 r->dval = value;
225 r->str = hi_malloc(len+1);
226 if (r->str == NULL) {
227 freeReplyObject(r);
228 return NULL;
229 }
230
231 /* The double reply also has the original protocol string representing a
232 * double as a null terminated string. This way the caller does not need
233 * to format back for string conversion, especially since Redis does efforts
234 * to make the string more human readable avoiding the calssical double
235 * decimal string conversion artifacts. */
236 memcpy(r->str, str, len);
237 r->str[len] = '\0';
238
239 if (task->parent) {
240 parent = task->parent->obj;
241 assert(parent->type == REDIS_REPLY_ARRAY ||
242 parent->type == REDIS_REPLY_MAP ||
243 parent->type == REDIS_REPLY_SET);
244 parent->element[task->idx] = r;
245 }
246 return r;
247 }
248
createNilObject(const redisReadTask * task)249 static void *createNilObject(const redisReadTask *task) {
250 redisReply *r, *parent;
251
252 r = createReplyObject(REDIS_REPLY_NIL);
253 if (r == NULL)
254 return NULL;
255
256 if (task->parent) {
257 parent = task->parent->obj;
258 assert(parent->type == REDIS_REPLY_ARRAY ||
259 parent->type == REDIS_REPLY_MAP ||
260 parent->type == REDIS_REPLY_SET);
261 parent->element[task->idx] = r;
262 }
263 return r;
264 }
265
createBoolObject(const redisReadTask * task,int bval)266 static void *createBoolObject(const redisReadTask *task, int bval) {
267 redisReply *r, *parent;
268
269 r = createReplyObject(REDIS_REPLY_BOOL);
270 if (r == NULL)
271 return NULL;
272
273 r->integer = bval != 0;
274
275 if (task->parent) {
276 parent = task->parent->obj;
277 assert(parent->type == REDIS_REPLY_ARRAY ||
278 parent->type == REDIS_REPLY_MAP ||
279 parent->type == REDIS_REPLY_SET);
280 parent->element[task->idx] = r;
281 }
282 return r;
283 }
284
285 /* Return the number of digits of 'v' when converted to string in radix 10.
286 * Implementation borrowed from link in redis/src/util.c:string2ll(). */
countDigits(uint64_t v)287 static uint32_t countDigits(uint64_t v) {
288 uint32_t result = 1;
289 for (;;) {
290 if (v < 10) return result;
291 if (v < 100) return result + 1;
292 if (v < 1000) return result + 2;
293 if (v < 10000) return result + 3;
294 v /= 10000U;
295 result += 4;
296 }
297 }
298
299 /* Helper that calculates the bulk length given a certain string length. */
bulklen(size_t len)300 static size_t bulklen(size_t len) {
301 return 1+countDigits(len)+2+len+2;
302 }
303
redisvFormatCommand(char ** target,const char * format,va_list ap)304 int redisvFormatCommand(char **target, const char *format, va_list ap) {
305 const char *c = format;
306 char *cmd = NULL; /* final command */
307 int pos; /* position in final command */
308 sds curarg, newarg; /* current argument */
309 int touched = 0; /* was the current argument touched? */
310 char **curargv = NULL, **newargv = NULL;
311 int argc = 0;
312 int totlen = 0;
313 int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */
314 int j;
315
316 /* Abort if there is not target to set */
317 if (target == NULL)
318 return -1;
319
320 /* Build the command string accordingly to protocol */
321 curarg = sdsempty();
322 if (curarg == NULL)
323 return -1;
324
325 while(*c != '\0') {
326 if (*c != '%' || c[1] == '\0') {
327 if (*c == ' ') {
328 if (touched) {
329 newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
330 if (newargv == NULL) goto memory_err;
331 curargv = newargv;
332 curargv[argc++] = curarg;
333 totlen += bulklen(sdslen(curarg));
334
335 /* curarg is put in argv so it can be overwritten. */
336 curarg = sdsempty();
337 if (curarg == NULL) goto memory_err;
338 touched = 0;
339 }
340 } else {
341 newarg = sdscatlen(curarg,c,1);
342 if (newarg == NULL) goto memory_err;
343 curarg = newarg;
344 touched = 1;
345 }
346 } else {
347 char *arg;
348 size_t size;
349
350 /* Set newarg so it can be checked even if it is not touched. */
351 newarg = curarg;
352
353 switch(c[1]) {
354 case 's':
355 arg = va_arg(ap,char*);
356 size = strlen(arg);
357 if (size > 0)
358 newarg = sdscatlen(curarg,arg,size);
359 break;
360 case 'b':
361 arg = va_arg(ap,char*);
362 size = va_arg(ap,size_t);
363 if (size > 0)
364 newarg = sdscatlen(curarg,arg,size);
365 break;
366 case '%':
367 newarg = sdscat(curarg,"%");
368 break;
369 default:
370 /* Try to detect printf format */
371 {
372 static const char intfmts[] = "diouxX";
373 static const char flags[] = "#0-+ ";
374 char _format[16];
375 const char *_p = c+1;
376 size_t _l = 0;
377 va_list _cpy;
378
379 /* Flags */
380 while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++;
381
382 /* Field width */
383 while (*_p != '\0' && isdigit(*_p)) _p++;
384
385 /* Precision */
386 if (*_p == '.') {
387 _p++;
388 while (*_p != '\0' && isdigit(*_p)) _p++;
389 }
390
391 /* Copy va_list before consuming with va_arg */
392 va_copy(_cpy,ap);
393
394 /* Integer conversion (without modifiers) */
395 if (strchr(intfmts,*_p) != NULL) {
396 va_arg(ap,int);
397 goto fmt_valid;
398 }
399
400 /* Double conversion (without modifiers) */
401 if (strchr("eEfFgGaA",*_p) != NULL) {
402 va_arg(ap,double);
403 goto fmt_valid;
404 }
405
406 /* Size: char */
407 if (_p[0] == 'h' && _p[1] == 'h') {
408 _p += 2;
409 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
410 va_arg(ap,int); /* char gets promoted to int */
411 goto fmt_valid;
412 }
413 goto fmt_invalid;
414 }
415
416 /* Size: short */
417 if (_p[0] == 'h') {
418 _p += 1;
419 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
420 va_arg(ap,int); /* short gets promoted to int */
421 goto fmt_valid;
422 }
423 goto fmt_invalid;
424 }
425
426 /* Size: long long */
427 if (_p[0] == 'l' && _p[1] == 'l') {
428 _p += 2;
429 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
430 va_arg(ap,long long);
431 goto fmt_valid;
432 }
433 goto fmt_invalid;
434 }
435
436 /* Size: long */
437 if (_p[0] == 'l') {
438 _p += 1;
439 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
440 va_arg(ap,long);
441 goto fmt_valid;
442 }
443 goto fmt_invalid;
444 }
445
446 fmt_invalid:
447 va_end(_cpy);
448 goto format_err;
449
450 fmt_valid:
451 _l = (_p+1)-c;
452 if (_l < sizeof(_format)-2) {
453 memcpy(_format,c,_l);
454 _format[_l] = '\0';
455 newarg = sdscatvprintf(curarg,_format,_cpy);
456
457 /* Update current position (note: outer blocks
458 * increment c twice so compensate here) */
459 c = _p-1;
460 }
461
462 va_end(_cpy);
463 break;
464 }
465 }
466
467 if (newarg == NULL) goto memory_err;
468 curarg = newarg;
469
470 touched = 1;
471 c++;
472 }
473 c++;
474 }
475
476 /* Add the last argument if needed */
477 if (touched) {
478 newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
479 if (newargv == NULL) goto memory_err;
480 curargv = newargv;
481 curargv[argc++] = curarg;
482 totlen += bulklen(sdslen(curarg));
483 } else {
484 sdsfree(curarg);
485 }
486
487 /* Clear curarg because it was put in curargv or was free'd. */
488 curarg = NULL;
489
490 /* Add bytes needed to hold multi bulk count */
491 totlen += 1+countDigits(argc)+2;
492
493 /* Build the command at protocol level */
494 cmd = hi_malloc(totlen+1);
495 if (cmd == NULL) goto memory_err;
496
497 pos = sprintf(cmd,"*%d\r\n",argc);
498 for (j = 0; j < argc; j++) {
499 pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
500 memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
501 pos += sdslen(curargv[j]);
502 sdsfree(curargv[j]);
503 cmd[pos++] = '\r';
504 cmd[pos++] = '\n';
505 }
506 assert(pos == totlen);
507 cmd[pos] = '\0';
508
509 hi_free(curargv);
510 *target = cmd;
511 return totlen;
512
513 format_err:
514 error_type = -2;
515 goto cleanup;
516
517 memory_err:
518 error_type = -1;
519 goto cleanup;
520
521 cleanup:
522 if (curargv) {
523 while(argc--)
524 sdsfree(curargv[argc]);
525 hi_free(curargv);
526 }
527
528 sdsfree(curarg);
529 hi_free(cmd);
530
531 return error_type;
532 }
533
534 /* Format a command according to the Redis protocol. This function
535 * takes a format similar to printf:
536 *
537 * %s represents a C null terminated string you want to interpolate
538 * %b represents a binary safe string
539 *
540 * When using %b you need to provide both the pointer to the string
541 * and the length in bytes as a size_t. Examples:
542 *
543 * len = redisFormatCommand(target, "GET %s", mykey);
544 * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
545 */
redisFormatCommand(char ** target,const char * format,...)546 int redisFormatCommand(char **target, const char *format, ...) {
547 va_list ap;
548 int len;
549 va_start(ap,format);
550 len = redisvFormatCommand(target,format,ap);
551 va_end(ap);
552
553 /* The API says "-1" means bad result, but we now also return "-2" in some
554 * cases. Force the return value to always be -1. */
555 if (len < 0)
556 len = -1;
557
558 return len;
559 }
560
561 /* Format a command according to the Redis protocol using an sds string and
562 * sdscatfmt for the processing of arguments. This function takes the
563 * number of arguments, an array with arguments and an array with their
564 * lengths. If the latter is set to NULL, strlen will be used to compute the
565 * argument lengths.
566 */
redisFormatSdsCommandArgv(sds * target,int argc,const char ** argv,const size_t * argvlen)567 int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,
568 const size_t *argvlen)
569 {
570 sds cmd, aux;
571 unsigned long long totlen;
572 int j;
573 size_t len;
574
575 /* Abort on a NULL target */
576 if (target == NULL)
577 return -1;
578
579 /* Calculate our total size */
580 totlen = 1+countDigits(argc)+2;
581 for (j = 0; j < argc; j++) {
582 len = argvlen ? argvlen[j] : strlen(argv[j]);
583 totlen += bulklen(len);
584 }
585
586 /* Use an SDS string for command construction */
587 cmd = sdsempty();
588 if (cmd == NULL)
589 return -1;
590
591 /* We already know how much storage we need */
592 aux = sdsMakeRoomFor(cmd, totlen);
593 if (aux == NULL) {
594 sdsfree(cmd);
595 return -1;
596 }
597
598 cmd = aux;
599
600 /* Construct command */
601 cmd = sdscatfmt(cmd, "*%i\r\n", argc);
602 for (j=0; j < argc; j++) {
603 len = argvlen ? argvlen[j] : strlen(argv[j]);
604 cmd = sdscatfmt(cmd, "$%u\r\n", len);
605 cmd = sdscatlen(cmd, argv[j], len);
606 cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1);
607 }
608
609 assert(sdslen(cmd)==totlen);
610
611 *target = cmd;
612 return totlen;
613 }
614
redisFreeSdsCommand(sds cmd)615 void redisFreeSdsCommand(sds cmd) {
616 sdsfree(cmd);
617 }
618
619 /* Format a command according to the Redis protocol. This function takes the
620 * number of arguments, an array with arguments and an array with their
621 * lengths. If the latter is set to NULL, strlen will be used to compute the
622 * argument lengths.
623 */
redisFormatCommandArgv(char ** target,int argc,const char ** argv,const size_t * argvlen)624 int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
625 char *cmd = NULL; /* final command */
626 int pos; /* position in final command */
627 size_t len;
628 int totlen, j;
629
630 /* Abort on a NULL target */
631 if (target == NULL)
632 return -1;
633
634 /* Calculate number of bytes needed for the command */
635 totlen = 1+countDigits(argc)+2;
636 for (j = 0; j < argc; j++) {
637 len = argvlen ? argvlen[j] : strlen(argv[j]);
638 totlen += bulklen(len);
639 }
640
641 /* Build the command at protocol level */
642 cmd = hi_malloc(totlen+1);
643 if (cmd == NULL)
644 return -1;
645
646 pos = sprintf(cmd,"*%d\r\n",argc);
647 for (j = 0; j < argc; j++) {
648 len = argvlen ? argvlen[j] : strlen(argv[j]);
649 pos += sprintf(cmd+pos,"$%zu\r\n",len);
650 memcpy(cmd+pos,argv[j],len);
651 pos += len;
652 cmd[pos++] = '\r';
653 cmd[pos++] = '\n';
654 }
655 assert(pos == totlen);
656 cmd[pos] = '\0';
657
658 *target = cmd;
659 return totlen;
660 }
661
redisFreeCommand(char * cmd)662 void redisFreeCommand(char *cmd) {
663 hi_free(cmd);
664 }
665
__redisSetError(redisContext * c,int type,const char * str)666 void __redisSetError(redisContext *c, int type, const char *str) {
667 size_t len;
668
669 c->err = type;
670 if (str != NULL) {
671 len = strlen(str);
672 len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);
673 memcpy(c->errstr,str,len);
674 c->errstr[len] = '\0';
675 } else {
676 /* Only REDIS_ERR_IO may lack a description! */
677 assert(type == REDIS_ERR_IO);
678 strerror_r(errno, c->errstr, sizeof(c->errstr));
679 }
680 }
681
redisReaderCreate(void)682 redisReader *redisReaderCreate(void) {
683 return redisReaderCreateWithFunctions(&defaultFunctions);
684 }
685
redisPushAutoFree(void * privdata,void * reply)686 static void redisPushAutoFree(void *privdata, void *reply) {
687 (void)privdata;
688 freeReplyObject(reply);
689 }
690
redisContextInit(void)691 static redisContext *redisContextInit(void) {
692 redisContext *c;
693
694 c = hi_calloc(1, sizeof(*c));
695 if (c == NULL)
696 return NULL;
697
698 c->funcs = &redisContextDefaultFuncs;
699
700 c->obuf = sdsempty();
701 c->reader = redisReaderCreate();
702 c->fd = REDIS_INVALID_FD;
703
704 if (c->obuf == NULL || c->reader == NULL) {
705 redisFree(c);
706 return NULL;
707 }
708
709 return c;
710 }
711
redisFree(redisContext * c)712 void redisFree(redisContext *c) {
713 if (c == NULL)
714 return;
715 redisNetClose(c);
716
717 sdsfree(c->obuf);
718 redisReaderFree(c->reader);
719 hi_free(c->tcp.host);
720 hi_free(c->tcp.source_addr);
721 hi_free(c->unix_sock.path);
722 hi_free(c->connect_timeout);
723 hi_free(c->command_timeout);
724 hi_free(c->saddr);
725
726 if (c->privdata && c->free_privdata)
727 c->free_privdata(c->privdata);
728
729 if (c->funcs->free_privctx)
730 c->funcs->free_privctx(c->privctx);
731
732 memset(c, 0xff, sizeof(*c));
733 hi_free(c);
734 }
735
redisFreeKeepFd(redisContext * c)736 redisFD redisFreeKeepFd(redisContext *c) {
737 redisFD fd = c->fd;
738 c->fd = REDIS_INVALID_FD;
739 redisFree(c);
740 return fd;
741 }
742
redisReconnect(redisContext * c)743 int redisReconnect(redisContext *c) {
744 c->err = 0;
745 memset(c->errstr, '\0', strlen(c->errstr));
746
747 if (c->privctx && c->funcs->free_privctx) {
748 c->funcs->free_privctx(c->privctx);
749 c->privctx = NULL;
750 }
751
752 redisNetClose(c);
753
754 sdsfree(c->obuf);
755 redisReaderFree(c->reader);
756
757 c->obuf = sdsempty();
758 c->reader = redisReaderCreate();
759
760 if (c->obuf == NULL || c->reader == NULL) {
761 __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
762 return REDIS_ERR;
763 }
764
765 int ret = REDIS_ERR;
766 if (c->connection_type == REDIS_CONN_TCP) {
767 ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
768 c->connect_timeout, c->tcp.source_addr);
769 } else if (c->connection_type == REDIS_CONN_UNIX) {
770 ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout);
771 } else {
772 /* Something bad happened here and shouldn't have. There isn't
773 enough information in the context to reconnect. */
774 __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect");
775 ret = REDIS_ERR;
776 }
777
778 if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
779 redisContextSetTimeout(c, *c->command_timeout);
780 }
781
782 return ret;
783 }
784
redisConnectWithOptions(const redisOptions * options)785 redisContext *redisConnectWithOptions(const redisOptions *options) {
786 redisContext *c = redisContextInit();
787 if (c == NULL) {
788 return NULL;
789 }
790 if (!(options->options & REDIS_OPT_NONBLOCK)) {
791 c->flags |= REDIS_BLOCK;
792 }
793 if (options->options & REDIS_OPT_REUSEADDR) {
794 c->flags |= REDIS_REUSEADDR;
795 }
796 if (options->options & REDIS_OPT_NOAUTOFREE) {
797 c->flags |= REDIS_NO_AUTO_FREE;
798 }
799
800 /* Set any user supplied RESP3 PUSH handler or use freeReplyObject
801 * as a default unless specifically flagged that we don't want one. */
802 if (options->push_cb != NULL)
803 redisSetPushCallback(c, options->push_cb);
804 else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE))
805 redisSetPushCallback(c, redisPushAutoFree);
806
807 c->privdata = options->privdata;
808 c->free_privdata = options->free_privdata;
809
810 if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK ||
811 redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) {
812 __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
813 return c;
814 }
815
816 if (options->type == REDIS_CONN_TCP) {
817 redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
818 options->endpoint.tcp.port, options->connect_timeout,
819 options->endpoint.tcp.source_addr);
820 } else if (options->type == REDIS_CONN_UNIX) {
821 redisContextConnectUnix(c, options->endpoint.unix_socket,
822 options->connect_timeout);
823 } else if (options->type == REDIS_CONN_USERFD) {
824 c->fd = options->endpoint.fd;
825 c->flags |= REDIS_CONNECTED;
826 } else {
827 // Unknown type - FIXME - FREE
828 return NULL;
829 }
830
831 if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
832 redisContextSetTimeout(c, *options->command_timeout);
833 }
834
835 return c;
836 }
837
838 /* Connect to a Redis instance. On error the field error in the returned
839 * context will be set to the return value of the error function.
840 * When no set of reply functions is given, the default set will be used. */
redisConnect(const char * ip,int port)841 redisContext *redisConnect(const char *ip, int port) {
842 redisOptions options = {0};
843 REDIS_OPTIONS_SET_TCP(&options, ip, port);
844 return redisConnectWithOptions(&options);
845 }
846
redisConnectWithTimeout(const char * ip,int port,const struct timeval tv)847 redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
848 redisOptions options = {0};
849 REDIS_OPTIONS_SET_TCP(&options, ip, port);
850 options.connect_timeout = &tv;
851 return redisConnectWithOptions(&options);
852 }
853
redisConnectNonBlock(const char * ip,int port)854 redisContext *redisConnectNonBlock(const char *ip, int port) {
855 redisOptions options = {0};
856 REDIS_OPTIONS_SET_TCP(&options, ip, port);
857 options.options |= REDIS_OPT_NONBLOCK;
858 return redisConnectWithOptions(&options);
859 }
860
redisConnectBindNonBlock(const char * ip,int port,const char * source_addr)861 redisContext *redisConnectBindNonBlock(const char *ip, int port,
862 const char *source_addr) {
863 redisOptions options = {0};
864 REDIS_OPTIONS_SET_TCP(&options, ip, port);
865 options.endpoint.tcp.source_addr = source_addr;
866 options.options |= REDIS_OPT_NONBLOCK;
867 return redisConnectWithOptions(&options);
868 }
869
redisConnectBindNonBlockWithReuse(const char * ip,int port,const char * source_addr)870 redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
871 const char *source_addr) {
872 redisOptions options = {0};
873 REDIS_OPTIONS_SET_TCP(&options, ip, port);
874 options.endpoint.tcp.source_addr = source_addr;
875 options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR;
876 return redisConnectWithOptions(&options);
877 }
878
redisConnectUnix(const char * path)879 redisContext *redisConnectUnix(const char *path) {
880 redisOptions options = {0};
881 REDIS_OPTIONS_SET_UNIX(&options, path);
882 return redisConnectWithOptions(&options);
883 }
884
redisConnectUnixWithTimeout(const char * path,const struct timeval tv)885 redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
886 redisOptions options = {0};
887 REDIS_OPTIONS_SET_UNIX(&options, path);
888 options.connect_timeout = &tv;
889 return redisConnectWithOptions(&options);
890 }
891
redisConnectUnixNonBlock(const char * path)892 redisContext *redisConnectUnixNonBlock(const char *path) {
893 redisOptions options = {0};
894 REDIS_OPTIONS_SET_UNIX(&options, path);
895 options.options |= REDIS_OPT_NONBLOCK;
896 return redisConnectWithOptions(&options);
897 }
898
redisConnectFd(redisFD fd)899 redisContext *redisConnectFd(redisFD fd) {
900 redisOptions options = {0};
901 options.type = REDIS_CONN_USERFD;
902 options.endpoint.fd = fd;
903 return redisConnectWithOptions(&options);
904 }
905
906 /* Set read/write timeout on a blocking socket. */
redisSetTimeout(redisContext * c,const struct timeval tv)907 int redisSetTimeout(redisContext *c, const struct timeval tv) {
908 if (c->flags & REDIS_BLOCK)
909 return redisContextSetTimeout(c,tv);
910 return REDIS_ERR;
911 }
912
913 /* Enable connection KeepAlive. */
redisEnableKeepAlive(redisContext * c)914 int redisEnableKeepAlive(redisContext *c) {
915 if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)
916 return REDIS_ERR;
917 return REDIS_OK;
918 }
919
920 /* Set a user provided RESP3 PUSH handler and return any old one set. */
redisSetPushCallback(redisContext * c,redisPushFn * fn)921 redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) {
922 redisPushFn *old = c->push_cb;
923 c->push_cb = fn;
924 return old;
925 }
926
927 /* Use this function to handle a read event on the descriptor. It will try
928 * and read some bytes from the socket and feed them to the reply parser.
929 *
930 * After this function is called, you may use redisGetReplyFromReader to
931 * see if there is a reply available. */
redisBufferRead(redisContext * c)932 int redisBufferRead(redisContext *c) {
933 char buf[1024*16];
934 int nread;
935
936 /* Return early when the context has seen an error. */
937 if (c->err)
938 return REDIS_ERR;
939
940 nread = c->funcs->read(c, buf, sizeof(buf));
941 if (nread > 0) {
942 if (redisReaderFeed(c->reader, buf, nread) != REDIS_OK) {
943 __redisSetError(c, c->reader->err, c->reader->errstr);
944 return REDIS_ERR;
945 } else {
946 }
947 } else if (nread < 0) {
948 return REDIS_ERR;
949 }
950 return REDIS_OK;
951 }
952
953 /* Write the output buffer to the socket.
954 *
955 * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
956 * successfully written to the socket. When the buffer is empty after the
957 * write operation, "done" is set to 1 (if given).
958 *
959 * Returns REDIS_ERR if an error occurred trying to write and sets
960 * c->errstr to hold the appropriate error string.
961 */
redisBufferWrite(redisContext * c,int * done)962 int redisBufferWrite(redisContext *c, int *done) {
963
964 /* Return early when the context has seen an error. */
965 if (c->err)
966 return REDIS_ERR;
967
968 if (sdslen(c->obuf) > 0) {
969 ssize_t nwritten = c->funcs->write(c);
970 if (nwritten < 0) {
971 return REDIS_ERR;
972 } else if (nwritten > 0) {
973 if (nwritten == (ssize_t)sdslen(c->obuf)) {
974 sdsfree(c->obuf);
975 c->obuf = sdsempty();
976 if (c->obuf == NULL)
977 goto oom;
978 } else {
979 if (sdsrange(c->obuf,nwritten,-1) < 0) goto oom;
980 }
981 }
982 }
983 if (done != NULL) *done = (sdslen(c->obuf) == 0);
984 return REDIS_OK;
985
986 oom:
987 __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
988 return REDIS_ERR;
989 }
990
991 /* Internal helper function to try and get a reply from the reader,
992 * or set an error in the context otherwise. */
redisGetReplyFromReader(redisContext * c,void ** reply)993 int redisGetReplyFromReader(redisContext *c, void **reply) {
994 if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {
995 __redisSetError(c,c->reader->err,c->reader->errstr);
996 return REDIS_ERR;
997 }
998
999 return REDIS_OK;
1000 }
1001
1002 /* Internal helper that returns 1 if the reply was a RESP3 PUSH
1003 * message and we handled it with a user-provided callback. */
redisHandledPushReply(redisContext * c,void * reply)1004 static int redisHandledPushReply(redisContext *c, void *reply) {
1005 if (reply && c->push_cb && redisIsPushReply(reply)) {
1006 c->push_cb(c->privdata, reply);
1007 return 1;
1008 }
1009
1010 return 0;
1011 }
1012
redisGetReply(redisContext * c,void ** reply)1013 int redisGetReply(redisContext *c, void **reply) {
1014 int wdone = 0;
1015 void *aux = NULL;
1016
1017 /* Try to read pending replies */
1018 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1019 return REDIS_ERR;
1020
1021 /* For the blocking context, flush output buffer and read reply */
1022 if (aux == NULL && c->flags & REDIS_BLOCK) {
1023 /* Write until done */
1024 do {
1025 if (redisBufferWrite(c,&wdone) == REDIS_ERR)
1026 return REDIS_ERR;
1027 } while (!wdone);
1028
1029 /* Read until there is a reply */
1030 do {
1031 if (redisBufferRead(c) == REDIS_ERR)
1032 return REDIS_ERR;
1033
1034 /* We loop here in case the user has specified a RESP3
1035 * PUSH handler (e.g. for client tracking). */
1036 do {
1037 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1038 return REDIS_ERR;
1039 } while (redisHandledPushReply(c, aux));
1040 } while (aux == NULL);
1041 }
1042
1043 /* Set reply or free it if we were passed NULL */
1044 if (reply != NULL) {
1045 *reply = aux;
1046 } else {
1047 freeReplyObject(aux);
1048 }
1049
1050 return REDIS_OK;
1051 }
1052
1053
1054 /* Helper function for the redisAppendCommand* family of functions.
1055 *
1056 * Write a formatted command to the output buffer. When this family
1057 * is used, you need to call redisGetReply yourself to retrieve
1058 * the reply (or replies in pub/sub).
1059 */
__redisAppendCommand(redisContext * c,const char * cmd,size_t len)1060 int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {
1061 sds newbuf;
1062
1063 newbuf = sdscatlen(c->obuf,cmd,len);
1064 if (newbuf == NULL) {
1065 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1066 return REDIS_ERR;
1067 }
1068
1069 c->obuf = newbuf;
1070 return REDIS_OK;
1071 }
1072
redisAppendFormattedCommand(redisContext * c,const char * cmd,size_t len)1073 int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) {
1074
1075 if (__redisAppendCommand(c, cmd, len) != REDIS_OK) {
1076 return REDIS_ERR;
1077 }
1078
1079 return REDIS_OK;
1080 }
1081
redisvAppendCommand(redisContext * c,const char * format,va_list ap)1082 int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1083 char *cmd;
1084 int len;
1085
1086 len = redisvFormatCommand(&cmd,format,ap);
1087 if (len == -1) {
1088 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1089 return REDIS_ERR;
1090 } else if (len == -2) {
1091 __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string");
1092 return REDIS_ERR;
1093 }
1094
1095 if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1096 hi_free(cmd);
1097 return REDIS_ERR;
1098 }
1099
1100 hi_free(cmd);
1101 return REDIS_OK;
1102 }
1103
redisAppendCommand(redisContext * c,const char * format,...)1104 int redisAppendCommand(redisContext *c, const char *format, ...) {
1105 va_list ap;
1106 int ret;
1107
1108 va_start(ap,format);
1109 ret = redisvAppendCommand(c,format,ap);
1110 va_end(ap);
1111 return ret;
1112 }
1113
redisAppendCommandArgv(redisContext * c,int argc,const char ** argv,const size_t * argvlen)1114 int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1115 sds cmd;
1116 int len;
1117
1118 len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
1119 if (len == -1) {
1120 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1121 return REDIS_ERR;
1122 }
1123
1124 if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1125 sdsfree(cmd);
1126 return REDIS_ERR;
1127 }
1128
1129 sdsfree(cmd);
1130 return REDIS_OK;
1131 }
1132
1133 /* Helper function for the redisCommand* family of functions.
1134 *
1135 * Write a formatted command to the output buffer. If the given context is
1136 * blocking, immediately read the reply into the "reply" pointer. When the
1137 * context is non-blocking, the "reply" pointer will not be used and the
1138 * command is simply appended to the write buffer.
1139 *
1140 * Returns the reply when a reply was successfully retrieved. Returns NULL
1141 * otherwise. When NULL is returned in a blocking context, the error field
1142 * in the context will be set.
1143 */
__redisBlockForReply(redisContext * c)1144 static void *__redisBlockForReply(redisContext *c) {
1145 void *reply;
1146
1147 if (c->flags & REDIS_BLOCK) {
1148 if (redisGetReply(c,&reply) != REDIS_OK)
1149 return NULL;
1150 return reply;
1151 }
1152 return NULL;
1153 }
1154
redisvCommand(redisContext * c,const char * format,va_list ap)1155 void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1156 if (redisvAppendCommand(c,format,ap) != REDIS_OK)
1157 return NULL;
1158 return __redisBlockForReply(c);
1159 }
1160
redisCommand(redisContext * c,const char * format,...)1161 void *redisCommand(redisContext *c, const char *format, ...) {
1162 va_list ap;
1163 va_start(ap,format);
1164 void *reply = redisvCommand(c,format,ap);
1165 va_end(ap);
1166 return reply;
1167 }
1168
redisCommandArgv(redisContext * c,int argc,const char ** argv,const size_t * argvlen)1169 void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1170 if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)
1171 return NULL;
1172 return __redisBlockForReply(c);
1173 }
1174