hiredis 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,199 @@
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
+ #ifndef __HIREDIS_H
35
+ #define __HIREDIS_H
36
+ #include "read.h"
37
+ #include <stdarg.h> /* for va_list */
38
+ #include <sys/time.h> /* for struct timeval */
39
+ #include <stdint.h> /* uintXX_t, etc */
40
+ #include "sds.h" /* for sds */
41
+
42
+ #define HIREDIS_MAJOR 0
43
+ #define HIREDIS_MINOR 14
44
+ #define HIREDIS_PATCH 0
45
+ #define HIREDIS_SONAME 0.14
46
+
47
+ /* Connection type can be blocking or non-blocking and is set in the
48
+ * least significant bit of the flags field in redisContext. */
49
+ #define REDIS_BLOCK 0x1
50
+
51
+ /* Connection may be disconnected before being free'd. The second bit
52
+ * in the flags field is set when the context is connected. */
53
+ #define REDIS_CONNECTED 0x2
54
+
55
+ /* The async API might try to disconnect cleanly and flush the output
56
+ * buffer and read all subsequent replies before disconnecting.
57
+ * This flag means no new commands can come in and the connection
58
+ * should be terminated once all replies have been read. */
59
+ #define REDIS_DISCONNECTING 0x4
60
+
61
+ /* Flag specific to the async API which means that the context should be clean
62
+ * up as soon as possible. */
63
+ #define REDIS_FREEING 0x8
64
+
65
+ /* Flag that is set when an async callback is executed. */
66
+ #define REDIS_IN_CALLBACK 0x10
67
+
68
+ /* Flag that is set when the async context has one or more subscriptions. */
69
+ #define REDIS_SUBSCRIBED 0x20
70
+
71
+ /* Flag that is set when monitor mode is active */
72
+ #define REDIS_MONITORING 0x40
73
+
74
+ /* Flag that is set when we should set SO_REUSEADDR before calling bind() */
75
+ #define REDIS_REUSEADDR 0x80
76
+
77
+ #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
78
+
79
+ /* number of times we retry to connect in the case of EADDRNOTAVAIL and
80
+ * SO_REUSEADDR is being used. */
81
+ #define REDIS_CONNECT_RETRIES 10
82
+
83
+ #ifdef __cplusplus
84
+ extern "C" {
85
+ #endif
86
+
87
+ /* This is the reply object returned by redisCommand() */
88
+ typedef struct redisReply {
89
+ int type; /* REDIS_REPLY_* */
90
+ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
91
+ size_t len; /* Length of string */
92
+ char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
93
+ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
94
+ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
95
+ } redisReply;
96
+
97
+ redisReader *redisReaderCreate(void);
98
+
99
+ /* Function to free the reply objects hiredis returns by default. */
100
+ void freeReplyObject(void *reply);
101
+
102
+ /* Functions to format a command according to the protocol. */
103
+ int redisvFormatCommand(char **target, const char *format, va_list ap);
104
+ int redisFormatCommand(char **target, const char *format, ...);
105
+ int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
106
+ int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
107
+ void redisFreeCommand(char *cmd);
108
+ void redisFreeSdsCommand(sds cmd);
109
+
110
+ enum redisConnectionType {
111
+ REDIS_CONN_TCP,
112
+ REDIS_CONN_UNIX
113
+ };
114
+
115
+ /* Context for a connection to Redis */
116
+ typedef struct redisContext {
117
+ int err; /* Error flags, 0 when there is no error */
118
+ char errstr[128]; /* String representation of error when applicable */
119
+ int fd;
120
+ int flags;
121
+ char *obuf; /* Write buffer */
122
+ redisReader *reader; /* Protocol reader */
123
+
124
+ enum redisConnectionType connection_type;
125
+ struct timeval *timeout;
126
+
127
+ struct {
128
+ char *host;
129
+ char *source_addr;
130
+ int port;
131
+ } tcp;
132
+
133
+ struct {
134
+ char *path;
135
+ } unix_sock;
136
+
137
+ } redisContext;
138
+
139
+ redisContext *redisConnect(const char *ip, int port);
140
+ redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
141
+ redisContext *redisConnectNonBlock(const char *ip, int port);
142
+ redisContext *redisConnectBindNonBlock(const char *ip, int port,
143
+ const char *source_addr);
144
+ redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
145
+ const char *source_addr);
146
+ redisContext *redisConnectUnix(const char *path);
147
+ redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
148
+ redisContext *redisConnectUnixNonBlock(const char *path);
149
+ redisContext *redisConnectFd(int fd);
150
+
151
+ /**
152
+ * Reconnect the given context using the saved information.
153
+ *
154
+ * This re-uses the exact same connect options as in the initial connection.
155
+ * host, ip (or path), timeout and bind address are reused,
156
+ * flags are used unmodified from the existing context.
157
+ *
158
+ * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
159
+ */
160
+ int redisReconnect(redisContext *c);
161
+
162
+ int redisSetTimeout(redisContext *c, const struct timeval tv);
163
+ int redisEnableKeepAlive(redisContext *c);
164
+ void redisFree(redisContext *c);
165
+ int redisFreeKeepFd(redisContext *c);
166
+ int redisBufferRead(redisContext *c);
167
+ int redisBufferWrite(redisContext *c, int *done);
168
+
169
+ /* In a blocking context, this function first checks if there are unconsumed
170
+ * replies to return and returns one if so. Otherwise, it flushes the output
171
+ * buffer to the socket and reads until it has a reply. In a non-blocking
172
+ * context, it will return unconsumed replies until there are no more. */
173
+ int redisGetReply(redisContext *c, void **reply);
174
+ int redisGetReplyFromReader(redisContext *c, void **reply);
175
+
176
+ /* Write a formatted command to the output buffer. Use these functions in blocking mode
177
+ * to get a pipeline of commands. */
178
+ int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
179
+
180
+ /* Write a command to the output buffer. Use these functions in blocking mode
181
+ * to get a pipeline of commands. */
182
+ int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
183
+ int redisAppendCommand(redisContext *c, const char *format, ...);
184
+ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
185
+
186
+ /* Issue a command to Redis. In a blocking context, it is identical to calling
187
+ * redisAppendCommand, followed by redisGetReply. The function will return
188
+ * NULL if there was an error in performing the request, otherwise it will
189
+ * return the reply. In a non-blocking context, it is identical to calling
190
+ * only redisAppendCommand and will always return NULL. */
191
+ void *redisvCommand(redisContext *c, const char *format, va_list ap);
192
+ void *redisCommand(redisContext *c, const char *format, ...);
193
+ void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
194
+
195
+ #ifdef __cplusplus
196
+ }
197
+ #endif
198
+
199
+ #endif
@@ -0,0 +1,477 @@
1
+ /* Extracted from anet.c to work properly with Hiredis error reporting.
2
+ *
3
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
5
+ * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
6
+ * Jan-Erik Rediger <janerik at fnordig dot com>
7
+ *
8
+ * All rights reserved.
9
+ *
10
+ * Redistribution and use in source and binary forms, with or without
11
+ * modification, are permitted provided that the following conditions are met:
12
+ *
13
+ * * Redistributions of source code must retain the above copyright notice,
14
+ * this list of conditions and the following disclaimer.
15
+ * * Redistributions in binary form must reproduce the above copyright
16
+ * notice, this list of conditions and the following disclaimer in the
17
+ * documentation and/or other materials provided with the distribution.
18
+ * * Neither the name of Redis nor the names of its contributors may be used
19
+ * to endorse or promote products derived from this software without
20
+ * specific prior written permission.
21
+ *
22
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ * POSSIBILITY OF SUCH DAMAGE.
33
+ */
34
+
35
+ #include "fmacros.h"
36
+ #include <sys/types.h>
37
+ #include <sys/socket.h>
38
+ #include <sys/select.h>
39
+ #include <sys/un.h>
40
+ #include <netinet/in.h>
41
+ #include <netinet/tcp.h>
42
+ #include <arpa/inet.h>
43
+ #include <unistd.h>
44
+ #include <fcntl.h>
45
+ #include <string.h>
46
+ #include <netdb.h>
47
+ #include <errno.h>
48
+ #include <stdarg.h>
49
+ #include <stdio.h>
50
+ #include <poll.h>
51
+ #include <limits.h>
52
+ #include <stdlib.h>
53
+
54
+ #include "net.h"
55
+ #include "sds.h"
56
+
57
+ /* Defined in hiredis.c */
58
+ void __redisSetError(redisContext *c, int type, const char *str);
59
+
60
+ static void redisContextCloseFd(redisContext *c) {
61
+ if (c && c->fd >= 0) {
62
+ close(c->fd);
63
+ c->fd = -1;
64
+ }
65
+ }
66
+
67
+ static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
68
+ int errorno = errno; /* snprintf() may change errno */
69
+ char buf[128] = { 0 };
70
+ size_t len = 0;
71
+
72
+ if (prefix != NULL)
73
+ len = snprintf(buf,sizeof(buf),"%s: ",prefix);
74
+ strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
75
+ __redisSetError(c,type,buf);
76
+ }
77
+
78
+ static int redisSetReuseAddr(redisContext *c) {
79
+ int on = 1;
80
+ if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
81
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
82
+ redisContextCloseFd(c);
83
+ return REDIS_ERR;
84
+ }
85
+ return REDIS_OK;
86
+ }
87
+
88
+ static int redisCreateSocket(redisContext *c, int type) {
89
+ int s;
90
+ if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
91
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
92
+ return REDIS_ERR;
93
+ }
94
+ c->fd = s;
95
+ if (type == AF_INET) {
96
+ if (redisSetReuseAddr(c) == REDIS_ERR) {
97
+ return REDIS_ERR;
98
+ }
99
+ }
100
+ return REDIS_OK;
101
+ }
102
+
103
+ static int redisSetBlocking(redisContext *c, int blocking) {
104
+ int flags;
105
+
106
+ /* Set the socket nonblocking.
107
+ * Note that fcntl(2) for F_GETFL and F_SETFL can't be
108
+ * interrupted by a signal. */
109
+ if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
110
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
111
+ redisContextCloseFd(c);
112
+ return REDIS_ERR;
113
+ }
114
+
115
+ if (blocking)
116
+ flags &= ~O_NONBLOCK;
117
+ else
118
+ flags |= O_NONBLOCK;
119
+
120
+ if (fcntl(c->fd, F_SETFL, flags) == -1) {
121
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
122
+ redisContextCloseFd(c);
123
+ return REDIS_ERR;
124
+ }
125
+ return REDIS_OK;
126
+ }
127
+
128
+ int redisKeepAlive(redisContext *c, int interval) {
129
+ int val = 1;
130
+ int fd = c->fd;
131
+
132
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
133
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
134
+ return REDIS_ERR;
135
+ }
136
+
137
+ val = interval;
138
+
139
+ #if defined(__APPLE__) && defined(__MACH__)
140
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
141
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
142
+ return REDIS_ERR;
143
+ }
144
+ #else
145
+ #if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
146
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
147
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
148
+ return REDIS_ERR;
149
+ }
150
+
151
+ val = interval/3;
152
+ if (val == 0) val = 1;
153
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
154
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
155
+ return REDIS_ERR;
156
+ }
157
+
158
+ val = 3;
159
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
160
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
161
+ return REDIS_ERR;
162
+ }
163
+ #endif
164
+ #endif
165
+
166
+ return REDIS_OK;
167
+ }
168
+
169
+ static int redisSetTcpNoDelay(redisContext *c) {
170
+ int yes = 1;
171
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
172
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
173
+ redisContextCloseFd(c);
174
+ return REDIS_ERR;
175
+ }
176
+ return REDIS_OK;
177
+ }
178
+
179
+ #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
180
+
181
+ static int redisContextTimeoutMsec(redisContext *c, long *result)
182
+ {
183
+ const struct timeval *timeout = c->timeout;
184
+ long msec = -1;
185
+
186
+ /* Only use timeout when not NULL. */
187
+ if (timeout != NULL) {
188
+ if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
189
+ *result = msec;
190
+ return REDIS_ERR;
191
+ }
192
+
193
+ msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
194
+
195
+ if (msec < 0 || msec > INT_MAX) {
196
+ msec = INT_MAX;
197
+ }
198
+ }
199
+
200
+ *result = msec;
201
+ return REDIS_OK;
202
+ }
203
+
204
+ static int redisContextWaitReady(redisContext *c, long msec) {
205
+ struct pollfd wfd[1];
206
+
207
+ wfd[0].fd = c->fd;
208
+ wfd[0].events = POLLOUT;
209
+
210
+ if (errno == EINPROGRESS) {
211
+ int res;
212
+
213
+ if ((res = poll(wfd, 1, msec)) == -1) {
214
+ __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
215
+ redisContextCloseFd(c);
216
+ return REDIS_ERR;
217
+ } else if (res == 0) {
218
+ errno = ETIMEDOUT;
219
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
220
+ redisContextCloseFd(c);
221
+ return REDIS_ERR;
222
+ }
223
+
224
+ if (redisCheckSocketError(c) != REDIS_OK)
225
+ return REDIS_ERR;
226
+
227
+ return REDIS_OK;
228
+ }
229
+
230
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
231
+ redisContextCloseFd(c);
232
+ return REDIS_ERR;
233
+ }
234
+
235
+ int redisCheckSocketError(redisContext *c) {
236
+ int err = 0;
237
+ socklen_t errlen = sizeof(err);
238
+
239
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
240
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
241
+ return REDIS_ERR;
242
+ }
243
+
244
+ if (err) {
245
+ errno = err;
246
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
247
+ return REDIS_ERR;
248
+ }
249
+
250
+ return REDIS_OK;
251
+ }
252
+
253
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
254
+ if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
255
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
256
+ return REDIS_ERR;
257
+ }
258
+ if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
259
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
260
+ return REDIS_ERR;
261
+ }
262
+ return REDIS_OK;
263
+ }
264
+
265
+ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
266
+ const struct timeval *timeout,
267
+ const char *source_addr) {
268
+ int s, rv, n;
269
+ char _port[6]; /* strlen("65535"); */
270
+ struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
271
+ int blocking = (c->flags & REDIS_BLOCK);
272
+ int reuseaddr = (c->flags & REDIS_REUSEADDR);
273
+ int reuses = 0;
274
+ long timeout_msec = -1;
275
+
276
+ servinfo = NULL;
277
+ c->connection_type = REDIS_CONN_TCP;
278
+ c->tcp.port = port;
279
+
280
+ /* We need to take possession of the passed parameters
281
+ * to make them reusable for a reconnect.
282
+ * We also carefully check we don't free data we already own,
283
+ * as in the case of the reconnect method.
284
+ *
285
+ * This is a bit ugly, but atleast it works and doesn't leak memory.
286
+ **/
287
+ if (c->tcp.host != addr) {
288
+ free(c->tcp.host);
289
+
290
+ c->tcp.host = strdup(addr);
291
+ }
292
+
293
+ if (timeout) {
294
+ if (c->timeout != timeout) {
295
+ if (c->timeout == NULL)
296
+ c->timeout = malloc(sizeof(struct timeval));
297
+
298
+ memcpy(c->timeout, timeout, sizeof(struct timeval));
299
+ }
300
+ } else {
301
+ free(c->timeout);
302
+ c->timeout = NULL;
303
+ }
304
+
305
+ if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
306
+ __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
307
+ goto error;
308
+ }
309
+
310
+ if (source_addr == NULL) {
311
+ free(c->tcp.source_addr);
312
+ c->tcp.source_addr = NULL;
313
+ } else if (c->tcp.source_addr != source_addr) {
314
+ free(c->tcp.source_addr);
315
+ c->tcp.source_addr = strdup(source_addr);
316
+ }
317
+
318
+ snprintf(_port, 6, "%d", port);
319
+ memset(&hints,0,sizeof(hints));
320
+ hints.ai_family = AF_INET;
321
+ hints.ai_socktype = SOCK_STREAM;
322
+
323
+ /* Try with IPv6 if no IPv4 address was found. We do it in this order since
324
+ * in a Redis client you can't afford to test if you have IPv6 connectivity
325
+ * as this would add latency to every connect. Otherwise a more sensible
326
+ * route could be: Use IPv6 if both addresses are available and there is IPv6
327
+ * connectivity. */
328
+ if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
329
+ hints.ai_family = AF_INET6;
330
+ if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
331
+ __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
332
+ return REDIS_ERR;
333
+ }
334
+ }
335
+ for (p = servinfo; p != NULL; p = p->ai_next) {
336
+ addrretry:
337
+ if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
338
+ continue;
339
+
340
+ c->fd = s;
341
+ if (redisSetBlocking(c,0) != REDIS_OK)
342
+ goto error;
343
+ if (c->tcp.source_addr) {
344
+ int bound = 0;
345
+ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
346
+ if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
347
+ char buf[128];
348
+ snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
349
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
350
+ goto error;
351
+ }
352
+
353
+ if (reuseaddr) {
354
+ n = 1;
355
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
356
+ sizeof(n)) < 0) {
357
+ freeaddrinfo(bservinfo);
358
+ goto error;
359
+ }
360
+ }
361
+
362
+ for (b = bservinfo; b != NULL; b = b->ai_next) {
363
+ if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
364
+ bound = 1;
365
+ break;
366
+ }
367
+ }
368
+ freeaddrinfo(bservinfo);
369
+ if (!bound) {
370
+ char buf[128];
371
+ snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
372
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
373
+ goto error;
374
+ }
375
+ }
376
+ if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
377
+ if (errno == EHOSTUNREACH) {
378
+ redisContextCloseFd(c);
379
+ continue;
380
+ } else if (errno == EINPROGRESS && !blocking) {
381
+ /* This is ok. */
382
+ } else if (errno == EADDRNOTAVAIL && reuseaddr) {
383
+ if (++reuses >= REDIS_CONNECT_RETRIES) {
384
+ goto error;
385
+ } else {
386
+ redisContextCloseFd(c);
387
+ goto addrretry;
388
+ }
389
+ } else {
390
+ if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
391
+ goto error;
392
+ }
393
+ }
394
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
395
+ goto error;
396
+ if (redisSetTcpNoDelay(c) != REDIS_OK)
397
+ goto error;
398
+
399
+ c->flags |= REDIS_CONNECTED;
400
+ rv = REDIS_OK;
401
+ goto end;
402
+ }
403
+ if (p == NULL) {
404
+ char buf[128];
405
+ snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
406
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
407
+ goto error;
408
+ }
409
+
410
+ error:
411
+ rv = REDIS_ERR;
412
+ end:
413
+ if(servinfo) {
414
+ freeaddrinfo(servinfo);
415
+ }
416
+
417
+ return rv; // Need to return REDIS_OK if alright
418
+ }
419
+
420
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port,
421
+ const struct timeval *timeout) {
422
+ return _redisContextConnectTcp(c, addr, port, timeout, NULL);
423
+ }
424
+
425
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
426
+ const struct timeval *timeout,
427
+ const char *source_addr) {
428
+ return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
429
+ }
430
+
431
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
432
+ int blocking = (c->flags & REDIS_BLOCK);
433
+ struct sockaddr_un sa;
434
+ long timeout_msec = -1;
435
+
436
+ if (redisCreateSocket(c,AF_UNIX) < 0)
437
+ return REDIS_ERR;
438
+ if (redisSetBlocking(c,0) != REDIS_OK)
439
+ return REDIS_ERR;
440
+
441
+ c->connection_type = REDIS_CONN_UNIX;
442
+ if (c->unix_sock.path != path)
443
+ c->unix_sock.path = strdup(path);
444
+
445
+ if (timeout) {
446
+ if (c->timeout != timeout) {
447
+ if (c->timeout == NULL)
448
+ c->timeout = malloc(sizeof(struct timeval));
449
+
450
+ memcpy(c->timeout, timeout, sizeof(struct timeval));
451
+ }
452
+ } else {
453
+ free(c->timeout);
454
+ c->timeout = NULL;
455
+ }
456
+
457
+ if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
458
+ return REDIS_ERR;
459
+
460
+ sa.sun_family = AF_UNIX;
461
+ strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
462
+ if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
463
+ if (errno == EINPROGRESS && !blocking) {
464
+ /* This is ok. */
465
+ } else {
466
+ if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
467
+ return REDIS_ERR;
468
+ }
469
+ }
470
+
471
+ /* Reset socket to be blocking after connect(2). */
472
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
473
+ return REDIS_ERR;
474
+
475
+ c->flags |= REDIS_CONNECTED;
476
+ return REDIS_OK;
477
+ }