Coverage Report

Created: 2024-02-25 06:15

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