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