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