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