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