rugged-redis 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,223 @@
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 13
44
+ #define HIREDIS_PATCH 3
45
+ #define HIREDIS_SONAME 0.13
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
+ /* strerror_r has two completely different prototypes and behaviors
84
+ * depending on system issues, so we need to operate on the error buffer
85
+ * differently depending on which strerror_r we're using. */
86
+ #ifndef _GNU_SOURCE
87
+ /* "regular" POSIX strerror_r that does the right thing. */
88
+ #define __redis_strerror_r(errno, buf, len) \
89
+ do { \
90
+ strerror_r((errno), (buf), (len)); \
91
+ } while (0)
92
+ #else
93
+ /* "bad" GNU strerror_r we need to clean up after. */
94
+ #define __redis_strerror_r(errno, buf, len) \
95
+ do { \
96
+ char *err_str = strerror_r((errno), (buf), (len)); \
97
+ /* If return value _isn't_ the start of the buffer we passed in, \
98
+ * then GNU strerror_r returned an internal static buffer and we \
99
+ * need to copy the result into our private buffer. */ \
100
+ if (err_str != (buf)) { \
101
+ strncpy((buf), err_str, ((len) - 1)); \
102
+ (buf)[(len)-1] = '\0'; \
103
+ } \
104
+ } while (0)
105
+ #endif
106
+
107
+ #ifdef __cplusplus
108
+ extern "C" {
109
+ #endif
110
+
111
+ /* This is the reply object returned by redisCommand() */
112
+ typedef struct redisReply {
113
+ int type; /* REDIS_REPLY_* */
114
+ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
115
+ size_t len; /* Length of string */
116
+ char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
117
+ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
118
+ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
119
+ } redisReply;
120
+
121
+ redisReader *redisReaderCreate(void);
122
+
123
+ /* Function to free the reply objects hiredis returns by default. */
124
+ void freeReplyObject(void *reply);
125
+
126
+ /* Functions to format a command according to the protocol. */
127
+ int redisvFormatCommand(char **target, const char *format, va_list ap);
128
+ int redisFormatCommand(char **target, const char *format, ...);
129
+ int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
130
+ int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
131
+ void redisFreeCommand(char *cmd);
132
+ void redisFreeSdsCommand(sds cmd);
133
+
134
+ enum redisConnectionType {
135
+ REDIS_CONN_TCP,
136
+ REDIS_CONN_UNIX
137
+ };
138
+
139
+ /* Context for a connection to Redis */
140
+ typedef struct redisContext {
141
+ int err; /* Error flags, 0 when there is no error */
142
+ char errstr[128]; /* String representation of error when applicable */
143
+ int fd;
144
+ int flags;
145
+ char *obuf; /* Write buffer */
146
+ redisReader *reader; /* Protocol reader */
147
+
148
+ enum redisConnectionType connection_type;
149
+ struct timeval *timeout;
150
+
151
+ struct {
152
+ char *host;
153
+ char *source_addr;
154
+ int port;
155
+ } tcp;
156
+
157
+ struct {
158
+ char *path;
159
+ } unix_sock;
160
+
161
+ } redisContext;
162
+
163
+ redisContext *redisConnect(const char *ip, int port);
164
+ redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
165
+ redisContext *redisConnectNonBlock(const char *ip, int port);
166
+ redisContext *redisConnectBindNonBlock(const char *ip, int port,
167
+ const char *source_addr);
168
+ redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
169
+ const char *source_addr);
170
+ redisContext *redisConnectUnix(const char *path);
171
+ redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
172
+ redisContext *redisConnectUnixNonBlock(const char *path);
173
+ redisContext *redisConnectFd(int fd);
174
+
175
+ /**
176
+ * Reconnect the given context using the saved information.
177
+ *
178
+ * This re-uses the exact same connect options as in the initial connection.
179
+ * host, ip (or path), timeout and bind address are reused,
180
+ * flags are used unmodified from the existing context.
181
+ *
182
+ * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
183
+ */
184
+ int redisReconnect(redisContext *c);
185
+
186
+ int redisSetTimeout(redisContext *c, const struct timeval tv);
187
+ int redisEnableKeepAlive(redisContext *c);
188
+ void redisFree(redisContext *c);
189
+ int redisFreeKeepFd(redisContext *c);
190
+ int redisBufferRead(redisContext *c);
191
+ int redisBufferWrite(redisContext *c, int *done);
192
+
193
+ /* In a blocking context, this function first checks if there are unconsumed
194
+ * replies to return and returns one if so. Otherwise, it flushes the output
195
+ * buffer to the socket and reads until it has a reply. In a non-blocking
196
+ * context, it will return unconsumed replies until there are no more. */
197
+ int redisGetReply(redisContext *c, void **reply);
198
+ int redisGetReplyFromReader(redisContext *c, void **reply);
199
+
200
+ /* Write a formatted command to the output buffer. Use these functions in blocking mode
201
+ * to get a pipeline of commands. */
202
+ int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
203
+
204
+ /* Write a command to the output buffer. Use these functions in blocking mode
205
+ * to get a pipeline of commands. */
206
+ int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
207
+ int redisAppendCommand(redisContext *c, const char *format, ...);
208
+ int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
209
+
210
+ /* Issue a command to Redis. In a blocking context, it is identical to calling
211
+ * redisAppendCommand, followed by redisGetReply. The function will return
212
+ * NULL if there was an error in performing the request, otherwise it will
213
+ * return the reply. In a non-blocking context, it is identical to calling
214
+ * only redisAppendCommand and will always return NULL. */
215
+ void *redisvCommand(redisContext *c, const char *format, va_list ap);
216
+ void *redisCommand(redisContext *c, const char *format, ...);
217
+ void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
218
+
219
+ #ifdef __cplusplus
220
+ }
221
+ #endif
222
+
223
+ #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
+ __redis_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
+ #ifdef _OSX
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
+ val = interval;
147
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
148
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
149
+ return REDIS_ERR;
150
+ }
151
+
152
+ val = interval/3;
153
+ if (val == 0) val = 1;
154
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
155
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
156
+ return REDIS_ERR;
157
+ }
158
+
159
+ val = 3;
160
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
161
+ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
162
+ return REDIS_ERR;
163
+ }
164
+ #endif
165
+ #endif
166
+
167
+ return REDIS_OK;
168
+ }
169
+
170
+ static int redisSetTcpNoDelay(redisContext *c) {
171
+ int yes = 1;
172
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
173
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
174
+ redisContextCloseFd(c);
175
+ return REDIS_ERR;
176
+ }
177
+ return REDIS_OK;
178
+ }
179
+
180
+ #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
181
+
182
+ static int redisContextTimeoutMsec(redisContext *c, long *result)
183
+ {
184
+ const struct timeval *timeout = c->timeout;
185
+ long msec = -1;
186
+
187
+ /* Only use timeout when not NULL. */
188
+ if (timeout != NULL) {
189
+ if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
190
+ *result = msec;
191
+ return REDIS_ERR;
192
+ }
193
+
194
+ msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
195
+
196
+ if (msec < 0 || msec > INT_MAX) {
197
+ msec = INT_MAX;
198
+ }
199
+ }
200
+
201
+ *result = msec;
202
+ return REDIS_OK;
203
+ }
204
+
205
+ static int redisContextWaitReady(redisContext *c, long msec) {
206
+ struct pollfd wfd[1];
207
+
208
+ wfd[0].fd = c->fd;
209
+ wfd[0].events = POLLOUT;
210
+
211
+ if (errno == EINPROGRESS) {
212
+ int res;
213
+
214
+ if ((res = poll(wfd, 1, msec)) == -1) {
215
+ __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
216
+ redisContextCloseFd(c);
217
+ return REDIS_ERR;
218
+ } else if (res == 0) {
219
+ errno = ETIMEDOUT;
220
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
221
+ redisContextCloseFd(c);
222
+ return REDIS_ERR;
223
+ }
224
+
225
+ if (redisCheckSocketError(c) != REDIS_OK)
226
+ return REDIS_ERR;
227
+
228
+ return REDIS_OK;
229
+ }
230
+
231
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
232
+ redisContextCloseFd(c);
233
+ return REDIS_ERR;
234
+ }
235
+
236
+ int redisCheckSocketError(redisContext *c) {
237
+ int err = 0;
238
+ socklen_t errlen = sizeof(err);
239
+
240
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
241
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
242
+ return REDIS_ERR;
243
+ }
244
+
245
+ if (err) {
246
+ errno = err;
247
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
248
+ return REDIS_ERR;
249
+ }
250
+
251
+ return REDIS_OK;
252
+ }
253
+
254
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
255
+ if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
256
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
257
+ return REDIS_ERR;
258
+ }
259
+ if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
260
+ __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
261
+ return REDIS_ERR;
262
+ }
263
+ return REDIS_OK;
264
+ }
265
+
266
+ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
267
+ const struct timeval *timeout,
268
+ const char *source_addr) {
269
+ int s, rv, n;
270
+ char _port[6]; /* strlen("65535"); */
271
+ struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
272
+ int blocking = (c->flags & REDIS_BLOCK);
273
+ int reuseaddr = (c->flags & REDIS_REUSEADDR);
274
+ int reuses = 0;
275
+ long timeout_msec = -1;
276
+
277
+ servinfo = NULL;
278
+ c->connection_type = REDIS_CONN_TCP;
279
+ c->tcp.port = port;
280
+
281
+ /* We need to take possession of the passed parameters
282
+ * to make them reusable for a reconnect.
283
+ * We also carefully check we don't free data we already own,
284
+ * as in the case of the reconnect method.
285
+ *
286
+ * This is a bit ugly, but atleast it works and doesn't leak memory.
287
+ **/
288
+ if (c->tcp.host != addr) {
289
+ if (c->tcp.host)
290
+ free(c->tcp.host);
291
+
292
+ c->tcp.host = strdup(addr);
293
+ }
294
+
295
+ if (timeout) {
296
+ if (c->timeout != timeout) {
297
+ if (c->timeout == NULL)
298
+ c->timeout = malloc(sizeof(struct timeval));
299
+
300
+ memcpy(c->timeout, timeout, sizeof(struct timeval));
301
+ }
302
+ } else {
303
+ if (c->timeout)
304
+ free(c->timeout);
305
+ c->timeout = NULL;
306
+ }
307
+
308
+ if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
309
+ __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
310
+ goto error;
311
+ }
312
+
313
+ if (source_addr == NULL) {
314
+ free(c->tcp.source_addr);
315
+ c->tcp.source_addr = NULL;
316
+ } else if (c->tcp.source_addr != source_addr) {
317
+ free(c->tcp.source_addr);
318
+ c->tcp.source_addr = strdup(source_addr);
319
+ }
320
+
321
+ snprintf(_port, 6, "%d", port);
322
+ memset(&hints,0,sizeof(hints));
323
+ hints.ai_family = AF_INET;
324
+ hints.ai_socktype = SOCK_STREAM;
325
+
326
+ /* Try with IPv6 if no IPv4 address was found. We do it in this order since
327
+ * in a Redis client you can't afford to test if you have IPv6 connectivity
328
+ * as this would add latency to every connect. Otherwise a more sensible
329
+ * route could be: Use IPv6 if both addresses are available and there is IPv6
330
+ * connectivity. */
331
+ if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
332
+ hints.ai_family = AF_INET6;
333
+ if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
334
+ __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
335
+ return REDIS_ERR;
336
+ }
337
+ }
338
+ for (p = servinfo; p != NULL; p = p->ai_next) {
339
+ addrretry:
340
+ if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
341
+ continue;
342
+
343
+ c->fd = s;
344
+ if (redisSetBlocking(c,0) != REDIS_OK)
345
+ goto error;
346
+ if (c->tcp.source_addr) {
347
+ int bound = 0;
348
+ /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
349
+ if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
350
+ char buf[128];
351
+ snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
352
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
353
+ goto error;
354
+ }
355
+
356
+ if (reuseaddr) {
357
+ n = 1;
358
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
359
+ sizeof(n)) < 0) {
360
+ goto error;
361
+ }
362
+ }
363
+
364
+ for (b = bservinfo; b != NULL; b = b->ai_next) {
365
+ if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
366
+ bound = 1;
367
+ break;
368
+ }
369
+ }
370
+ freeaddrinfo(bservinfo);
371
+ if (!bound) {
372
+ char buf[128];
373
+ snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
374
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
375
+ goto error;
376
+ }
377
+ }
378
+ if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
379
+ if (errno == EHOSTUNREACH) {
380
+ redisContextCloseFd(c);
381
+ continue;
382
+ } else if (errno == EINPROGRESS && !blocking) {
383
+ /* This is ok. */
384
+ } else if (errno == EADDRNOTAVAIL && reuseaddr) {
385
+ if (++reuses >= REDIS_CONNECT_RETRIES) {
386
+ goto error;
387
+ } else {
388
+ redisContextCloseFd(c);
389
+ goto addrretry;
390
+ }
391
+ } else {
392
+ if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
393
+ goto error;
394
+ }
395
+ }
396
+ if (blocking && redisSetBlocking(c,1) != REDIS_OK)
397
+ goto error;
398
+ if (redisSetTcpNoDelay(c) != REDIS_OK)
399
+ goto error;
400
+
401
+ c->flags |= REDIS_CONNECTED;
402
+ rv = REDIS_OK;
403
+ goto end;
404
+ }
405
+ if (p == NULL) {
406
+ char buf[128];
407
+ snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
408
+ __redisSetError(c,REDIS_ERR_OTHER,buf);
409
+ goto error;
410
+ }
411
+
412
+ error:
413
+ rv = REDIS_ERR;
414
+ end:
415
+ freeaddrinfo(servinfo);
416
+ return rv; // Need to return REDIS_OK if alright
417
+ }
418
+
419
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port,
420
+ const struct timeval *timeout) {
421
+ return _redisContextConnectTcp(c, addr, port, timeout, NULL);
422
+ }
423
+
424
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
425
+ const struct timeval *timeout,
426
+ const char *source_addr) {
427
+ return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
428
+ }
429
+
430
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
431
+ int blocking = (c->flags & REDIS_BLOCK);
432
+ struct sockaddr_un sa;
433
+ long timeout_msec = -1;
434
+
435
+ if (redisCreateSocket(c,AF_LOCAL) < 0)
436
+ return REDIS_ERR;
437
+ if (redisSetBlocking(c,0) != REDIS_OK)
438
+ return REDIS_ERR;
439
+
440
+ c->connection_type = REDIS_CONN_UNIX;
441
+ if (c->unix_sock.path != path)
442
+ c->unix_sock.path = strdup(path);
443
+
444
+ if (timeout) {
445
+ if (c->timeout != timeout) {
446
+ if (c->timeout == NULL)
447
+ c->timeout = malloc(sizeof(struct timeval));
448
+
449
+ memcpy(c->timeout, timeout, sizeof(struct timeval));
450
+ }
451
+ } else {
452
+ if (c->timeout)
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_LOCAL;
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
+ }