File: | deps/hiredis/net.c |
Warning: | line 425, column 10 Although the value stored to 'rv' is used in the enclosing expression, the value is never actually read from 'rv' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 <fcntl.h> |
38 | #include <string.h> |
39 | #include <errno(*__errno_location ()).h> |
40 | #include <stdarg.h> |
41 | #include <stdio.h> |
42 | #include <limits.h> |
43 | #include <stdlib.h> |
44 | |
45 | #include "net.h" |
46 | #include "sds.h" |
47 | #include "sockcompat.h" |
48 | #include "win32.h" |
49 | |
50 | /* Defined in hiredis.c */ |
51 | void __redisSetError(redisContext *c, int type, const char *str); |
52 | |
53 | void redisNetClose(redisContext *c) { |
54 | if (c && c->fd != REDIS_INVALID_FD-1) { |
55 | close(c->fd); |
56 | c->fd = REDIS_INVALID_FD-1; |
57 | } |
58 | } |
59 | |
60 | ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) { |
61 | ssize_t nread = recv(c->fd, buf, bufcap, 0); |
62 | if (nread == -1) { |
63 | if ((errno(*__errno_location ()) == EWOULDBLOCK11 && !(c->flags & REDIS_BLOCK0x1)) || (errno(*__errno_location ()) == EINTR4)) { |
64 | /* Try again later */ |
65 | return 0; |
66 | } else if(errno(*__errno_location ()) == ETIMEDOUT110 && (c->flags & REDIS_BLOCK0x1)) { |
67 | /* especially in windows */ |
68 | __redisSetError(c, REDIS_ERR_TIMEOUT6, "recv timeout"); |
69 | return -1; |
70 | } else { |
71 | __redisSetError(c, REDIS_ERR_IO1, NULL((void*)0)); |
72 | return -1; |
73 | } |
74 | } else if (nread == 0) { |
75 | __redisSetError(c, REDIS_ERR_EOF3, "Server closed the connection"); |
76 | return -1; |
77 | } else { |
78 | return nread; |
79 | } |
80 | } |
81 | |
82 | ssize_t redisNetWrite(redisContext *c) { |
83 | ssize_t nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0); |
84 | if (nwritten < 0) { |
85 | if ((errno(*__errno_location ()) == EWOULDBLOCK11 && !(c->flags & REDIS_BLOCK0x1)) || (errno(*__errno_location ()) == EINTR4)) { |
86 | /* Try again later */ |
87 | } else { |
88 | __redisSetError(c, REDIS_ERR_IO1, NULL((void*)0)); |
89 | return -1; |
90 | } |
91 | } |
92 | return nwritten; |
93 | } |
94 | |
95 | static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { |
96 | int errorno = errno(*__errno_location ()); /* snprintf() may change errno */ |
97 | char buf[128] = { 0 }; |
98 | size_t len = 0; |
99 | |
100 | if (prefix != NULL((void*)0)) |
101 | len = snprintf(buf,sizeof(buf),"%s: ",prefix); |
102 | strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len); |
103 | __redisSetError(c,type,buf); |
104 | } |
105 | |
106 | static int redisSetReuseAddr(redisContext *c) { |
107 | int on = 1; |
108 | if (setsockopt(c->fd, SOL_SOCKET1, SO_REUSEADDR2, &on, sizeof(on)) == -1) { |
109 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0)); |
110 | redisNetClose(c); |
111 | return REDIS_ERR-1; |
112 | } |
113 | return REDIS_OK0; |
114 | } |
115 | |
116 | static int redisCreateSocket(redisContext *c, int type) { |
117 | redisFD s; |
118 | if ((s = socket(type, SOCK_STREAMSOCK_STREAM, 0)) == REDIS_INVALID_FD-1) { |
119 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0)); |
120 | return REDIS_ERR-1; |
121 | } |
122 | c->fd = s; |
123 | if (type == AF_INET2) { |
124 | if (redisSetReuseAddr(c) == REDIS_ERR-1) { |
125 | return REDIS_ERR-1; |
126 | } |
127 | } |
128 | return REDIS_OK0; |
129 | } |
130 | |
131 | static int redisSetBlocking(redisContext *c, int blocking) { |
132 | #ifndef _WIN32 |
133 | int flags; |
134 | |
135 | /* Set the socket nonblocking. |
136 | * Note that fcntl(2) for F_GETFL and F_SETFL can't be |
137 | * interrupted by a signal. */ |
138 | if ((flags = fcntl(c->fd, F_GETFL3)) == -1) { |
139 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"fcntl(F_GETFL)"); |
140 | redisNetClose(c); |
141 | return REDIS_ERR-1; |
142 | } |
143 | |
144 | if (blocking) |
145 | flags &= ~O_NONBLOCK04000; |
146 | else |
147 | flags |= O_NONBLOCK04000; |
148 | |
149 | if (fcntl(c->fd, F_SETFL4, flags) == -1) { |
150 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"fcntl(F_SETFL)"); |
151 | redisNetClose(c); |
152 | return REDIS_ERR-1; |
153 | } |
154 | #else |
155 | u_long mode = blocking ? 0 : 1; |
156 | if (ioctl(c->fd, FIONBIO, &mode) == -1) { |
157 | __redisSetErrorFromErrno(c, REDIS_ERR_IO1, "ioctl(FIONBIO)"); |
158 | redisNetClose(c); |
159 | return REDIS_ERR-1; |
160 | } |
161 | #endif /* _WIN32 */ |
162 | return REDIS_OK0; |
163 | } |
164 | |
165 | int redisKeepAlive(redisContext *c, int interval) { |
166 | int val = 1; |
167 | redisFD fd = c->fd; |
168 | |
169 | if (setsockopt(fd, SOL_SOCKET1, SO_KEEPALIVE9, &val, sizeof(val)) == -1){ |
170 | __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ()))); |
171 | return REDIS_ERR-1; |
172 | } |
173 | |
174 | val = interval; |
175 | |
176 | #if defined(__APPLE__) && defined(__MACH__) |
177 | if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) { |
178 | __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ()))); |
179 | return REDIS_ERR-1; |
180 | } |
181 | #else |
182 | #if defined(__GLIBC__2) && !defined(__FreeBSD_kernel__) |
183 | if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPIDLE4, &val, sizeof(val)) < 0) { |
184 | __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ()))); |
185 | return REDIS_ERR-1; |
186 | } |
187 | |
188 | val = interval/3; |
189 | if (val == 0) val = 1; |
190 | if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPINTVL5, &val, sizeof(val)) < 0) { |
191 | __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ()))); |
192 | return REDIS_ERR-1; |
193 | } |
194 | |
195 | val = 3; |
196 | if (setsockopt(fd, IPPROTO_TCPIPPROTO_TCP, TCP_KEEPCNT6, &val, sizeof(val)) < 0) { |
197 | __redisSetError(c,REDIS_ERR_OTHER2,strerror(errno(*__errno_location ()))); |
198 | return REDIS_ERR-1; |
199 | } |
200 | #endif |
201 | #endif |
202 | |
203 | return REDIS_OK0; |
204 | } |
205 | |
206 | int redisSetTcpNoDelay(redisContext *c) { |
207 | int yes = 1; |
208 | if (setsockopt(c->fd, IPPROTO_TCPIPPROTO_TCP, TCP_NODELAY1, &yes, sizeof(yes)) == -1) { |
209 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"setsockopt(TCP_NODELAY)"); |
210 | redisNetClose(c); |
211 | return REDIS_ERR-1; |
212 | } |
213 | return REDIS_OK0; |
214 | } |
215 | |
216 | #define __MAX_MSEC(((9223372036854775807L) - 999) / 1000) (((LONG_MAX9223372036854775807L) - 999) / 1000) |
217 | |
218 | static int redisContextTimeoutMsec(redisContext *c, long *result) |
219 | { |
220 | const struct timeval *timeout = c->connect_timeout; |
221 | long msec = -1; |
222 | |
223 | /* Only use timeout when not NULL. */ |
224 | if (timeout != NULL((void*)0)) { |
225 | if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC(((9223372036854775807L) - 999) / 1000)) { |
226 | *result = msec; |
227 | return REDIS_ERR-1; |
228 | } |
229 | |
230 | msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000); |
231 | |
232 | if (msec < 0 || msec > INT_MAX2147483647) { |
233 | msec = INT_MAX2147483647; |
234 | } |
235 | } |
236 | |
237 | *result = msec; |
238 | return REDIS_OK0; |
239 | } |
240 | |
241 | static int redisContextWaitReady(redisContext *c, long msec) { |
242 | struct pollfd wfd[1]; |
243 | |
244 | wfd[0].fd = c->fd; |
245 | wfd[0].events = POLLOUT0x004; |
246 | |
247 | if (errno(*__errno_location ()) == EINPROGRESS115) { |
248 | int res; |
249 | |
250 | if ((res = poll(wfd, 1, msec)) == -1) { |
251 | __redisSetErrorFromErrno(c, REDIS_ERR_IO1, "poll(2)"); |
252 | redisNetClose(c); |
253 | return REDIS_ERR-1; |
254 | } else if (res == 0) { |
255 | errno(*__errno_location ()) = ETIMEDOUT110; |
256 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0)); |
257 | redisNetClose(c); |
258 | return REDIS_ERR-1; |
259 | } |
260 | |
261 | if (redisCheckConnectDone(c, &res) != REDIS_OK0 || res == 0) { |
262 | redisCheckSocketError(c); |
263 | return REDIS_ERR-1; |
264 | } |
265 | |
266 | return REDIS_OK0; |
267 | } |
268 | |
269 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0)); |
270 | redisNetClose(c); |
271 | return REDIS_ERR-1; |
272 | } |
273 | |
274 | int redisCheckConnectDone(redisContext *c, int *completed) { |
275 | int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen); |
276 | if (rc == 0) { |
277 | *completed = 1; |
278 | return REDIS_OK0; |
279 | } |
280 | switch (errno(*__errno_location ())) { |
281 | case EISCONN106: |
282 | *completed = 1; |
283 | return REDIS_OK0; |
284 | case EALREADY114: |
285 | case EINPROGRESS115: |
286 | case EWOULDBLOCK11: |
287 | *completed = 0; |
288 | return REDIS_OK0; |
289 | default: |
290 | return REDIS_ERR-1; |
291 | } |
292 | } |
293 | |
294 | int redisCheckSocketError(redisContext *c) { |
295 | int err = 0, errno_saved = errno(*__errno_location ()); |
296 | socklen_t errlen = sizeof(err); |
297 | |
298 | if (getsockopt(c->fd, SOL_SOCKET1, SO_ERROR4, &err, &errlen) == -1) { |
299 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"getsockopt(SO_ERROR)"); |
300 | return REDIS_ERR-1; |
301 | } |
302 | |
303 | if (err == 0) { |
304 | err = errno_saved; |
305 | } |
306 | |
307 | if (err) { |
308 | errno(*__errno_location ()) = err; |
309 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,NULL((void*)0)); |
310 | return REDIS_ERR-1; |
311 | } |
312 | |
313 | return REDIS_OK0; |
314 | } |
315 | |
316 | int redisContextSetTimeout(redisContext *c, const struct timeval tv) { |
317 | const void *to_ptr = &tv; |
318 | size_t to_sz = sizeof(tv); |
319 | |
320 | if (setsockopt(c->fd,SOL_SOCKET1,SO_RCVTIMEO20,to_ptr,to_sz) == -1) { |
321 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"setsockopt(SO_RCVTIMEO)"); |
322 | return REDIS_ERR-1; |
323 | } |
324 | if (setsockopt(c->fd,SOL_SOCKET1,SO_SNDTIMEO21,to_ptr,to_sz) == -1) { |
325 | __redisSetErrorFromErrno(c,REDIS_ERR_IO1,"setsockopt(SO_SNDTIMEO)"); |
326 | return REDIS_ERR-1; |
327 | } |
328 | return REDIS_OK0; |
329 | } |
330 | |
331 | int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) { |
332 | /* Same timeval struct, short circuit */ |
333 | if (c->connect_timeout == timeout) |
334 | return REDIS_OK0; |
335 | |
336 | /* Allocate context timeval if we need to */ |
337 | if (c->connect_timeout == NULL((void*)0)) { |
338 | c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout)); |
339 | if (c->connect_timeout == NULL((void*)0)) |
340 | return REDIS_ERR-1; |
341 | } |
342 | |
343 | memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout)); |
344 | return REDIS_OK0; |
345 | } |
346 | |
347 | int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) { |
348 | /* Same timeval struct, short circuit */ |
349 | if (c->command_timeout == timeout) |
350 | return REDIS_OK0; |
351 | |
352 | /* Allocate context timeval if we need to */ |
353 | if (c->command_timeout == NULL((void*)0)) { |
354 | c->command_timeout = hi_malloc(sizeof(*c->command_timeout)); |
355 | if (c->command_timeout == NULL((void*)0)) |
356 | return REDIS_ERR-1; |
357 | } |
358 | |
359 | memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout)); |
360 | return REDIS_OK0; |
361 | } |
362 | |
363 | static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, |
364 | const struct timeval *timeout, |
365 | const char *source_addr) { |
366 | redisFD s; |
367 | int rv, n; |
368 | char _port[6]; /* strlen("65535"); */ |
369 | struct addrinfo hints, *servinfo, *bservinfo, *p, *b; |
370 | int blocking = (c->flags & REDIS_BLOCK0x1); |
371 | int reuseaddr = (c->flags & REDIS_REUSEADDR0x80); |
372 | int reuses = 0; |
373 | long timeout_msec = -1; |
374 | |
375 | servinfo = NULL((void*)0); |
376 | c->connection_type = REDIS_CONN_TCP; |
377 | c->tcp.port = port; |
378 | |
379 | /* We need to take possession of the passed parameters |
380 | * to make them reusable for a reconnect. |
381 | * We also carefully check we don't free data we already own, |
382 | * as in the case of the reconnect method. |
383 | * |
384 | * This is a bit ugly, but atleast it works and doesn't leak memory. |
385 | **/ |
386 | if (c->tcp.host != addr) { |
387 | hi_free(c->tcp.host); |
388 | |
389 | c->tcp.host = hi_strdup(addr); |
390 | if (c->tcp.host == NULL((void*)0)) |
391 | goto oom; |
392 | } |
393 | |
394 | if (timeout) { |
395 | if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR-1) |
396 | goto oom; |
397 | } else { |
398 | hi_free(c->connect_timeout); |
399 | c->connect_timeout = NULL((void*)0); |
400 | } |
401 | |
402 | if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK0) { |
403 | __redisSetError(c, REDIS_ERR_IO1, "Invalid timeout specified"); |
404 | goto error; |
405 | } |
406 | |
407 | if (source_addr == NULL((void*)0)) { |
408 | hi_free(c->tcp.source_addr); |
409 | c->tcp.source_addr = NULL((void*)0); |
410 | } else if (c->tcp.source_addr != source_addr) { |
411 | hi_free(c->tcp.source_addr); |
412 | c->tcp.source_addr = hi_strdup(source_addr); |
413 | } |
414 | |
415 | snprintf(_port, 6, "%d", port); |
416 | memset(&hints,0,sizeof(hints)); |
417 | hints.ai_family = AF_INET2; |
418 | hints.ai_socktype = SOCK_STREAMSOCK_STREAM; |
419 | |
420 | /* Try with IPv6 if no IPv4 address was found. We do it in this order since |
421 | * in a Redis client you can't afford to test if you have IPv6 connectivity |
422 | * as this would add latency to every connect. Otherwise a more sensible |
423 | * route could be: Use IPv6 if both addresses are available and there is IPv6 |
424 | * connectivity. */ |
425 | if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) { |
Although the value stored to 'rv' is used in the enclosing expression, the value is never actually read from 'rv' | |
426 | hints.ai_family = AF_INET610; |
427 | if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { |
428 | __redisSetError(c,REDIS_ERR_OTHER2,gai_strerror(rv)); |
429 | return REDIS_ERR-1; |
430 | } |
431 | } |
432 | for (p = servinfo; p != NULL((void*)0); p = p->ai_next) { |
433 | addrretry: |
434 | if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD-1) |
435 | continue; |
436 | |
437 | c->fd = s; |
438 | if (redisSetBlocking(c,0) != REDIS_OK0) |
439 | goto error; |
440 | if (c->tcp.source_addr) { |
441 | int bound = 0; |
442 | /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ |
443 | if ((rv = getaddrinfo(c->tcp.source_addr, NULL((void*)0), &hints, &bservinfo)) != 0) { |
444 | char buf[128]; |
445 | snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv)); |
446 | __redisSetError(c,REDIS_ERR_OTHER2,buf); |
447 | goto error; |
448 | } |
449 | |
450 | if (reuseaddr) { |
451 | n = 1; |
452 | if (setsockopt(s, SOL_SOCKET1, SO_REUSEADDR2, (char*) &n, |
453 | sizeof(n)) < 0) { |
454 | freeaddrinfo(bservinfo); |
455 | goto error; |
456 | } |
457 | } |
458 | |
459 | for (b = bservinfo; b != NULL((void*)0); b = b->ai_next) { |
460 | if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { |
461 | bound = 1; |
462 | break; |
463 | } |
464 | } |
465 | freeaddrinfo(bservinfo); |
466 | if (!bound) { |
467 | char buf[128]; |
468 | snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno(*__errno_location ()))); |
469 | __redisSetError(c,REDIS_ERR_OTHER2,buf); |
470 | goto error; |
471 | } |
472 | } |
473 | |
474 | /* For repeat connection */ |
475 | hi_free(c->saddr); |
476 | c->saddr = hi_malloc(p->ai_addrlen); |
477 | if (c->saddr == NULL((void*)0)) |
478 | goto oom; |
479 | |
480 | memcpy(c->saddr, p->ai_addr, p->ai_addrlen); |
481 | c->addrlen = p->ai_addrlen; |
482 | |
483 | if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { |
484 | if (errno(*__errno_location ()) == EHOSTUNREACH113) { |
485 | redisNetClose(c); |
486 | continue; |
487 | } else if (errno(*__errno_location ()) == EINPROGRESS115) { |
488 | if (blocking) { |
489 | goto wait_for_ready; |
490 | } |
491 | /* This is ok. |
492 | * Note that even when it's in blocking mode, we unset blocking |
493 | * for `connect()` |
494 | */ |
495 | } else if (errno(*__errno_location ()) == EADDRNOTAVAIL99 && reuseaddr) { |
496 | if (++reuses >= REDIS_CONNECT_RETRIES10) { |
497 | goto error; |
498 | } else { |
499 | redisNetClose(c); |
500 | goto addrretry; |
501 | } |
502 | } else { |
503 | wait_for_ready: |
504 | if (redisContextWaitReady(c,timeout_msec) != REDIS_OK0) |
505 | goto error; |
506 | if (redisSetTcpNoDelay(c) != REDIS_OK0) |
507 | goto error; |
508 | } |
509 | } |
510 | if (blocking && redisSetBlocking(c,1) != REDIS_OK0) |
511 | goto error; |
512 | |
513 | c->flags |= REDIS_CONNECTED0x2; |
514 | rv = REDIS_OK0; |
515 | goto end; |
516 | } |
517 | if (p == NULL((void*)0)) { |
518 | char buf[128]; |
519 | snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno(*__errno_location ()))); |
520 | __redisSetError(c,REDIS_ERR_OTHER2,buf); |
521 | goto error; |
522 | } |
523 | |
524 | oom: |
525 | __redisSetError(c, REDIS_ERR_OOM5, "Out of memory"); |
526 | error: |
527 | rv = REDIS_ERR-1; |
528 | end: |
529 | if(servinfo) { |
530 | freeaddrinfo(servinfo); |
531 | } |
532 | |
533 | return rv; // Need to return REDIS_OK if alright |
534 | } |
535 | |
536 | int redisContextConnectTcp(redisContext *c, const char *addr, int port, |
537 | const struct timeval *timeout) { |
538 | return _redisContextConnectTcp(c, addr, port, timeout, NULL((void*)0)); |
539 | } |
540 | |
541 | int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, |
542 | const struct timeval *timeout, |
543 | const char *source_addr) { |
544 | return _redisContextConnectTcp(c, addr, port, timeout, source_addr); |
545 | } |
546 | |
547 | int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { |
548 | #ifndef _WIN32 |
549 | int blocking = (c->flags & REDIS_BLOCK0x1); |
550 | struct sockaddr_un *sa; |
551 | long timeout_msec = -1; |
552 | |
553 | if (redisCreateSocket(c,AF_UNIX1) < 0) |
554 | return REDIS_ERR-1; |
555 | if (redisSetBlocking(c,0) != REDIS_OK0) |
556 | return REDIS_ERR-1; |
557 | |
558 | c->connection_type = REDIS_CONN_UNIX; |
559 | if (c->unix_sock.path != path) { |
560 | hi_free(c->unix_sock.path); |
561 | |
562 | c->unix_sock.path = hi_strdup(path); |
563 | if (c->unix_sock.path == NULL((void*)0)) |
564 | goto oom; |
565 | } |
566 | |
567 | if (timeout) { |
568 | if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR-1) |
569 | goto oom; |
570 | } else { |
571 | hi_free(c->connect_timeout); |
572 | c->connect_timeout = NULL((void*)0); |
573 | } |
574 | |
575 | if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK0) |
576 | return REDIS_ERR-1; |
577 | |
578 | /* Don't leak sockaddr if we're reconnecting */ |
579 | if (c->saddr) hi_free(c->saddr); |
580 | |
581 | sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un))); |
582 | if (sa == NULL((void*)0)) |
583 | goto oom; |
584 | |
585 | c->addrlen = sizeof(struct sockaddr_un); |
586 | sa->sun_family = AF_UNIX1; |
587 | strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1); |
588 | if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) { |
589 | if (errno(*__errno_location ()) == EINPROGRESS115 && !blocking) { |
590 | /* This is ok. */ |
591 | } else { |
592 | if (redisContextWaitReady(c,timeout_msec) != REDIS_OK0) |
593 | return REDIS_ERR-1; |
594 | } |
595 | } |
596 | |
597 | /* Reset socket to be blocking after connect(2). */ |
598 | if (blocking && redisSetBlocking(c,1) != REDIS_OK0) |
599 | return REDIS_ERR-1; |
600 | |
601 | c->flags |= REDIS_CONNECTED0x2; |
602 | return REDIS_OK0; |
603 | #else |
604 | /* We currently do not support Unix sockets for Windows. */ |
605 | /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */ |
606 | errno(*__errno_location ()) = EPROTONOSUPPORT93; |
607 | return REDIS_ERR-1; |
608 | #endif /* _WIN32 */ |
609 | oom: |
610 | __redisSetError(c, REDIS_ERR_OOM5, "Out of memory"); |
611 | return REDIS_ERR-1; |
612 | } |