diff options
Diffstat (limited to 'examples/redis-unstable/src/anet.c')
| -rw-r--r-- | examples/redis-unstable/src/anet.c | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/examples/redis-unstable/src/anet.c b/examples/redis-unstable/src/anet.c new file mode 100644 index 0000000..8b7b91e --- /dev/null +++ b/examples/redis-unstable/src/anet.c | |||
| @@ -0,0 +1,812 @@ | |||
| 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring | ||
| 2 | * | ||
| 3 | * Copyright (c) 2006-Present, Redis Ltd. | ||
| 4 | * All rights reserved. | ||
| 5 | * | ||
| 6 | * Licensed under your choice of (a) the Redis Source Available License 2.0 | ||
| 7 | * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the | ||
| 8 | * GNU Affero General Public License v3 (AGPLv3). | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include "fmacros.h" | ||
| 12 | |||
| 13 | #include <sys/types.h> | ||
| 14 | #include <sys/socket.h> | ||
| 15 | #include <sys/stat.h> | ||
| 16 | #include <sys/un.h> | ||
| 17 | #include <sys/time.h> | ||
| 18 | #include <netinet/in.h> | ||
| 19 | #include <netinet/tcp.h> | ||
| 20 | #include <arpa/inet.h> | ||
| 21 | #include <unistd.h> | ||
| 22 | #include <fcntl.h> | ||
| 23 | #include <string.h> | ||
| 24 | #include <netdb.h> | ||
| 25 | #include <errno.h> | ||
| 26 | #include <stdarg.h> | ||
| 27 | #include <stdio.h> | ||
| 28 | |||
| 29 | #include "anet.h" | ||
| 30 | #include "config.h" | ||
| 31 | #include "util.h" | ||
| 32 | |||
| 33 | #define UNUSED(x) (void)(x) | ||
| 34 | |||
| 35 | static void anetSetError(char *err, const char *fmt, ...) | ||
| 36 | { | ||
| 37 | va_list ap; | ||
| 38 | |||
| 39 | if (!err) return; | ||
| 40 | va_start(ap, fmt); | ||
| 41 | vsnprintf(err, ANET_ERR_LEN, fmt, ap); | ||
| 42 | va_end(ap); | ||
| 43 | } | ||
| 44 | |||
| 45 | int anetGetError(int fd) { | ||
| 46 | int sockerr = 0; | ||
| 47 | socklen_t errlen = sizeof(sockerr); | ||
| 48 | |||
| 49 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1) | ||
| 50 | sockerr = errno; | ||
| 51 | return sockerr; | ||
| 52 | } | ||
| 53 | |||
| 54 | int anetSetBlock(char *err, int fd, int non_block) { | ||
| 55 | int flags; | ||
| 56 | |||
| 57 | /* Set the socket blocking (if non_block is zero) or non-blocking. | ||
| 58 | * Note that fcntl(2) for F_GETFL and F_SETFL can't be | ||
| 59 | * interrupted by a signal. */ | ||
| 60 | if ((flags = fcntl(fd, F_GETFL)) == -1) { | ||
| 61 | anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); | ||
| 62 | return ANET_ERR; | ||
| 63 | } | ||
| 64 | |||
| 65 | /* Check if this flag has been set or unset, if so, | ||
| 66 | * then there is no need to call fcntl to set/unset it again. */ | ||
| 67 | if (!!(flags & O_NONBLOCK) == !!non_block) | ||
| 68 | return ANET_OK; | ||
| 69 | |||
| 70 | if (non_block) | ||
| 71 | flags |= O_NONBLOCK; | ||
| 72 | else | ||
| 73 | flags &= ~O_NONBLOCK; | ||
| 74 | |||
| 75 | if (fcntl(fd, F_SETFL, flags) == -1) { | ||
| 76 | anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); | ||
| 77 | return ANET_ERR; | ||
| 78 | } | ||
| 79 | return ANET_OK; | ||
| 80 | } | ||
| 81 | |||
| 82 | int anetNonBlock(char *err, int fd) { | ||
| 83 | return anetSetBlock(err,fd,1); | ||
| 84 | } | ||
| 85 | |||
| 86 | int anetBlock(char *err, int fd) { | ||
| 87 | return anetSetBlock(err,fd,0); | ||
| 88 | } | ||
| 89 | |||
| 90 | /* Enable the FD_CLOEXEC on the given fd to avoid fd leaks. | ||
| 91 | * This function should be invoked for fd's on specific places | ||
| 92 | * where fork + execve system calls are called. */ | ||
| 93 | int anetCloexec(int fd) { | ||
| 94 | int r; | ||
| 95 | int flags; | ||
| 96 | |||
| 97 | do { | ||
| 98 | r = fcntl(fd, F_GETFD); | ||
| 99 | } while (r == -1 && errno == EINTR); | ||
| 100 | |||
| 101 | if (r == -1 || (r & FD_CLOEXEC)) | ||
| 102 | return r; | ||
| 103 | |||
| 104 | flags = r | FD_CLOEXEC; | ||
| 105 | |||
| 106 | do { | ||
| 107 | r = fcntl(fd, F_SETFD, flags); | ||
| 108 | } while (r == -1 && errno == EINTR); | ||
| 109 | |||
| 110 | return r; | ||
| 111 | } | ||
| 112 | |||
| 113 | /* Enable TCP keep-alive mechanism to detect dead peers, | ||
| 114 | * TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT will be set accordingly. */ | ||
| 115 | int anetKeepAlive(char *err, int fd, int interval) | ||
| 116 | { | ||
| 117 | int enabled = 1; | ||
| 118 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled))) | ||
| 119 | { | ||
| 120 | anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); | ||
| 121 | return ANET_ERR; | ||
| 122 | } | ||
| 123 | |||
| 124 | int idle; | ||
| 125 | int intvl; | ||
| 126 | int cnt; | ||
| 127 | |||
| 128 | /* There are platforms that are expected to support the full mechanism of TCP keep-alive, | ||
| 129 | * we want the compiler to emit warnings of unused variables if the preprocessor directives | ||
| 130 | * somehow fail, and other than those platforms, just omit these warnings if they happen. | ||
| 131 | */ | ||
| 132 | #if !(defined(_AIX) || defined(__APPLE__) || defined(__DragonFly__) || \ | ||
| 133 | defined(__FreeBSD__) || defined(__illumos__) || defined(__linux__) || \ | ||
| 134 | defined(__NetBSD__) || defined(__sun)) | ||
| 135 | UNUSED(interval); | ||
| 136 | UNUSED(idle); | ||
| 137 | UNUSED(intvl); | ||
| 138 | UNUSED(cnt); | ||
| 139 | #endif | ||
| 140 | |||
| 141 | #ifdef __sun | ||
| 142 | /* The implementation of TCP keep-alive on Solaris/SmartOS is a bit unusual | ||
| 143 | * compared to other Unix-like systems. | ||
| 144 | * Thus, we need to specialize it on Solaris. | ||
| 145 | * | ||
| 146 | * There are two keep-alive mechanisms on Solaris: | ||
| 147 | * - By default, the first keep-alive probe is sent out after a TCP connection is idle for two hours. | ||
| 148 | * If the peer does not respond to the probe within eight minutes, the TCP connection is aborted. | ||
| 149 | * You can alter the interval for sending out the first probe using the socket option TCP_KEEPALIVE_THRESHOLD | ||
| 150 | * in milliseconds or TCP_KEEPIDLE in seconds. | ||
| 151 | * The system default is controlled by the TCP ndd parameter tcp_keepalive_interval. The minimum value is ten seconds. | ||
| 152 | * The maximum is ten days, while the default is two hours. If you receive no response to the probe, | ||
| 153 | * you can use the TCP_KEEPALIVE_ABORT_THRESHOLD socket option to change the time threshold for aborting a TCP connection. | ||
| 154 | * The option value is an unsigned integer in milliseconds. The value zero indicates that TCP should never time out and | ||
| 155 | * abort the connection when probing. The system default is controlled by the TCP ndd parameter tcp_keepalive_abort_interval. | ||
| 156 | * The default is eight minutes. | ||
| 157 | * | ||
| 158 | * - The second implementation is activated if socket option TCP_KEEPINTVL and/or TCP_KEEPCNT are set. | ||
| 159 | * The time between each consequent probes is set by TCP_KEEPINTVL in seconds. | ||
| 160 | * The minimum value is ten seconds. The maximum is ten days, while the default is two hours. | ||
| 161 | * The TCP connection will be aborted after certain amount of probes, which is set by TCP_KEEPCNT, without receiving response. | ||
| 162 | */ | ||
| 163 | |||
| 164 | idle = interval; | ||
| 165 | if (idle < 10) idle = 10; // kernel expects at least 10 seconds | ||
| 166 | if (idle > 10*24*60*60) idle = 10*24*60*60; // kernel expects at most 10 days | ||
| 167 | |||
| 168 | /* `TCP_KEEPIDLE`, `TCP_KEEPINTVL`, and `TCP_KEEPCNT` were not available on Solaris | ||
| 169 | * until version 11.4, but let's take a chance here. */ | ||
| 170 | #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) | ||
| 171 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) { | ||
| 172 | anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno)); | ||
| 173 | return ANET_ERR; | ||
| 174 | } | ||
| 175 | |||
| 176 | intvl = idle/3; | ||
| 177 | if (intvl < 10) intvl = 10; /* kernel expects at least 10 seconds */ | ||
| 178 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) { | ||
| 179 | anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno)); | ||
| 180 | return ANET_ERR; | ||
| 181 | } | ||
| 182 | |||
| 183 | cnt = 3; | ||
| 184 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) { | ||
| 185 | anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); | ||
| 186 | return ANET_ERR; | ||
| 187 | } | ||
| 188 | #else | ||
| 189 | /* Fall back to the first implementation of tcp-alive mechanism for older Solaris, | ||
| 190 | * simulate the tcp-alive mechanism on other platforms via `TCP_KEEPALIVE_THRESHOLD` + `TCP_KEEPALIVE_ABORT_THRESHOLD`. | ||
| 191 | */ | ||
| 192 | idle *= 1000; // kernel expects milliseconds | ||
| 193 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &idle, sizeof(idle))) { | ||
| 194 | anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno)); | ||
| 195 | return ANET_ERR; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* Note that the consequent probes will not be sent at equal intervals on Solaris, | ||
| 199 | * but will be sent using the exponential backoff algorithm. */ | ||
| 200 | int time_to_abort = idle; | ||
| 201 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort))) { | ||
| 202 | anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); | ||
| 203 | return ANET_ERR; | ||
| 204 | } | ||
| 205 | #endif | ||
| 206 | |||
| 207 | return ANET_OK; | ||
| 208 | |||
| 209 | #endif | ||
| 210 | |||
| 211 | #ifdef TCP_KEEPIDLE | ||
| 212 | /* Default settings are more or less garbage, with the keepalive time | ||
| 213 | * set to 7200 by default on Linux and other Unix-like systems. | ||
| 214 | * Modify settings to make the feature actually useful. */ | ||
| 215 | |||
| 216 | /* Send first probe after interval. */ | ||
| 217 | idle = interval; | ||
| 218 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) { | ||
| 219 | anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno)); | ||
| 220 | return ANET_ERR; | ||
| 221 | } | ||
| 222 | #elif defined(TCP_KEEPALIVE) | ||
| 223 | /* Darwin/macOS uses TCP_KEEPALIVE in place of TCP_KEEPIDLE. */ | ||
| 224 | idle = interval; | ||
| 225 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle))) { | ||
| 226 | anetSetError(err, "setsockopt TCP_KEEPALIVE: %s\n", strerror(errno)); | ||
| 227 | return ANET_ERR; | ||
| 228 | } | ||
| 229 | #endif | ||
| 230 | |||
| 231 | #ifdef TCP_KEEPINTVL | ||
| 232 | /* Send next probes after the specified interval. Note that we set the | ||
| 233 | * delay as interval / 3, as we send three probes before detecting | ||
| 234 | * an error (see the next setsockopt call). */ | ||
| 235 | intvl = interval/3; | ||
| 236 | if (intvl == 0) intvl = 1; | ||
| 237 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) { | ||
| 238 | anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno)); | ||
| 239 | return ANET_ERR; | ||
| 240 | } | ||
| 241 | #endif | ||
| 242 | |||
| 243 | #ifdef TCP_KEEPCNT | ||
| 244 | /* Consider the socket in error state after three we send three ACK | ||
| 245 | * probes without getting a reply. */ | ||
| 246 | cnt = 3; | ||
| 247 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) { | ||
| 248 | anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); | ||
| 249 | return ANET_ERR; | ||
| 250 | } | ||
| 251 | #endif | ||
| 252 | |||
| 253 | return ANET_OK; | ||
| 254 | } | ||
| 255 | |||
| 256 | static int anetSetTcpNoDelay(char *err, int fd, int val) | ||
| 257 | { | ||
| 258 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1) | ||
| 259 | { | ||
| 260 | anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); | ||
| 261 | return ANET_ERR; | ||
| 262 | } | ||
| 263 | return ANET_OK; | ||
| 264 | } | ||
| 265 | |||
| 266 | int anetEnableTcpNoDelay(char *err, int fd) | ||
| 267 | { | ||
| 268 | return anetSetTcpNoDelay(err, fd, 1); | ||
| 269 | } | ||
| 270 | |||
| 271 | int anetDisableTcpNoDelay(char *err, int fd) | ||
| 272 | { | ||
| 273 | return anetSetTcpNoDelay(err, fd, 0); | ||
| 274 | } | ||
| 275 | |||
| 276 | /* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified | ||
| 277 | * number of milliseconds, or disable it if the 'ms' argument is zero. */ | ||
| 278 | int anetSendTimeout(char *err, int fd, long long ms) { | ||
| 279 | struct timeval tv; | ||
| 280 | |||
| 281 | tv.tv_sec = ms/1000; | ||
| 282 | tv.tv_usec = (ms%1000)*1000; | ||
| 283 | if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) { | ||
| 284 | anetSetError(err, "setsockopt SO_SNDTIMEO: %s", strerror(errno)); | ||
| 285 | return ANET_ERR; | ||
| 286 | } | ||
| 287 | return ANET_OK; | ||
| 288 | } | ||
| 289 | |||
| 290 | /* Set the socket receive timeout (SO_RCVTIMEO socket option) to the specified | ||
| 291 | * number of milliseconds, or disable it if the 'ms' argument is zero. */ | ||
| 292 | int anetRecvTimeout(char *err, int fd, long long ms) { | ||
| 293 | struct timeval tv; | ||
| 294 | |||
| 295 | tv.tv_sec = ms/1000; | ||
| 296 | tv.tv_usec = (ms%1000)*1000; | ||
| 297 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { | ||
| 298 | anetSetError(err, "setsockopt SO_RCVTIMEO: %s", strerror(errno)); | ||
| 299 | return ANET_ERR; | ||
| 300 | } | ||
| 301 | return ANET_OK; | ||
| 302 | } | ||
| 303 | |||
| 304 | /* Resolve the hostname "host" and set the string representation of the | ||
| 305 | * IP address into the buffer pointed by "ipbuf". | ||
| 306 | * | ||
| 307 | * If flags is set to ANET_IP_ONLY the function only resolves hostnames | ||
| 308 | * that are actually already IPv4 or IPv6 addresses. This turns the function | ||
| 309 | * into a validating / normalizing function. | ||
| 310 | * | ||
| 311 | * If the flag ANET_PREFER_IPV4 is set, IPv4 is preferred over IPv6. | ||
| 312 | * If the flag ANET_PREFER_IPV6 is set, IPv6 is preferred over IPv4. | ||
| 313 | * */ | ||
| 314 | int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, | ||
| 315 | int flags) | ||
| 316 | { | ||
| 317 | struct addrinfo hints, *info; | ||
| 318 | int rv; | ||
| 319 | |||
| 320 | memset(&hints,0,sizeof(hints)); | ||
| 321 | if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST; | ||
| 322 | hints.ai_family = AF_UNSPEC; | ||
| 323 | if (flags & ANET_PREFER_IPV4 && !(flags & ANET_PREFER_IPV6)) { | ||
| 324 | hints.ai_family = AF_INET; | ||
| 325 | } else if (flags & ANET_PREFER_IPV6 && !(flags & ANET_PREFER_IPV4)) { | ||
| 326 | hints.ai_family = AF_INET6; | ||
| 327 | } | ||
| 328 | hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */ | ||
| 329 | |||
| 330 | rv = getaddrinfo(host, NULL, &hints, &info); | ||
| 331 | if (rv != 0 && hints.ai_family != AF_UNSPEC) { | ||
| 332 | /* Try the other IP version. */ | ||
| 333 | hints.ai_family = (hints.ai_family == AF_INET) ? AF_INET6 : AF_INET; | ||
| 334 | rv = getaddrinfo(host, NULL, &hints, &info); | ||
| 335 | } | ||
| 336 | if (rv != 0) { | ||
| 337 | anetSetError(err, "%s", gai_strerror(rv)); | ||
| 338 | return ANET_ERR; | ||
| 339 | } | ||
| 340 | if (info->ai_family == AF_INET) { | ||
| 341 | struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr; | ||
| 342 | inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len); | ||
| 343 | } else { | ||
| 344 | struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr; | ||
| 345 | inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len); | ||
| 346 | } | ||
| 347 | |||
| 348 | freeaddrinfo(info); | ||
| 349 | return ANET_OK; | ||
| 350 | } | ||
| 351 | |||
| 352 | static int anetSetReuseAddr(char *err, int fd) { | ||
| 353 | int yes = 1; | ||
| 354 | /* Make sure connection-intensive things like the redis benchmark | ||
| 355 | * will be able to close/open sockets a zillion of times */ | ||
| 356 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { | ||
| 357 | anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); | ||
| 358 | return ANET_ERR; | ||
| 359 | } | ||
| 360 | return ANET_OK; | ||
| 361 | } | ||
| 362 | |||
| 363 | static int anetCreateSocket(char *err, int domain) { | ||
| 364 | int s; | ||
| 365 | if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { | ||
| 366 | anetSetError(err, "creating socket: %s", strerror(errno)); | ||
| 367 | return ANET_ERR; | ||
| 368 | } | ||
| 369 | |||
| 370 | /* Make sure connection-intensive things like the redis benchmark | ||
| 371 | * will be able to close/open sockets a zillion of times */ | ||
| 372 | if (anetSetReuseAddr(err,s) == ANET_ERR) { | ||
| 373 | close(s); | ||
| 374 | return ANET_ERR; | ||
| 375 | } | ||
| 376 | return s; | ||
| 377 | } | ||
| 378 | |||
| 379 | #define ANET_CONNECT_NONE 0 | ||
| 380 | #define ANET_CONNECT_NONBLOCK 1 | ||
| 381 | #define ANET_CONNECT_BE_BINDING 2 /* Best effort binding. */ | ||
| 382 | static int anetTcpGenericConnect(char *err, const char *addr, int port, | ||
| 383 | const char *source_addr, int flags) | ||
| 384 | { | ||
| 385 | int s = ANET_ERR, rv; | ||
| 386 | char portstr[6]; /* strlen("65535") + 1; */ | ||
| 387 | struct addrinfo hints, *servinfo, *bservinfo, *p, *b; | ||
| 388 | |||
| 389 | snprintf(portstr,sizeof(portstr),"%d",port); | ||
| 390 | memset(&hints,0,sizeof(hints)); | ||
| 391 | hints.ai_family = AF_UNSPEC; | ||
| 392 | hints.ai_socktype = SOCK_STREAM; | ||
| 393 | |||
| 394 | if ((rv = getaddrinfo(addr,portstr,&hints,&servinfo)) != 0) { | ||
| 395 | anetSetError(err, "%s", gai_strerror(rv)); | ||
| 396 | return ANET_ERR; | ||
| 397 | } | ||
| 398 | for (p = servinfo; p != NULL; p = p->ai_next) { | ||
| 399 | /* Try to create the socket and to connect it. | ||
| 400 | * If we fail in the socket() call, or on connect(), we retry with | ||
| 401 | * the next entry in servinfo. */ | ||
| 402 | if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) | ||
| 403 | continue; | ||
| 404 | if (anetSetReuseAddr(err,s) == ANET_ERR) goto error; | ||
| 405 | if (flags & ANET_CONNECT_NONBLOCK && anetNonBlock(err,s) != ANET_OK) | ||
| 406 | goto error; | ||
| 407 | if (source_addr) { | ||
| 408 | int bound = 0; | ||
| 409 | /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ | ||
| 410 | if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0) | ||
| 411 | { | ||
| 412 | anetSetError(err, "%s", gai_strerror(rv)); | ||
| 413 | goto error; | ||
| 414 | } | ||
| 415 | for (b = bservinfo; b != NULL; b = b->ai_next) { | ||
| 416 | if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { | ||
| 417 | bound = 1; | ||
| 418 | break; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | freeaddrinfo(bservinfo); | ||
| 422 | if (!bound) { | ||
| 423 | anetSetError(err, "bind: %s", strerror(errno)); | ||
| 424 | goto error; | ||
| 425 | } | ||
| 426 | } | ||
| 427 | if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { | ||
| 428 | /* If the socket is non-blocking, it is ok for connect() to | ||
| 429 | * return an EINPROGRESS error here. */ | ||
| 430 | if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK) | ||
| 431 | goto end; | ||
| 432 | close(s); | ||
| 433 | s = ANET_ERR; | ||
| 434 | continue; | ||
| 435 | } | ||
| 436 | |||
| 437 | /* If we ended an iteration of the for loop without errors, we | ||
| 438 | * have a connected socket. Let's return to the caller. */ | ||
| 439 | goto end; | ||
| 440 | } | ||
| 441 | if (p == NULL) | ||
| 442 | anetSetError(err, "creating socket: %s", strerror(errno)); | ||
| 443 | |||
| 444 | error: | ||
| 445 | if (s != ANET_ERR) { | ||
| 446 | close(s); | ||
| 447 | s = ANET_ERR; | ||
| 448 | } | ||
| 449 | |||
| 450 | end: | ||
| 451 | freeaddrinfo(servinfo); | ||
| 452 | |||
| 453 | /* Handle best effort binding: if a binding address was used, but it is | ||
| 454 | * not possible to create a socket, try again without a binding address. */ | ||
| 455 | if (s == ANET_ERR && source_addr && (flags & ANET_CONNECT_BE_BINDING)) { | ||
| 456 | return anetTcpGenericConnect(err,addr,port,NULL,flags); | ||
| 457 | } else { | ||
| 458 | return s; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | int anetTcpNonBlockConnect(char *err, const char *addr, int port) | ||
| 463 | { | ||
| 464 | return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK); | ||
| 465 | } | ||
| 466 | |||
| 467 | int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, | ||
| 468 | const char *source_addr) | ||
| 469 | { | ||
| 470 | return anetTcpGenericConnect(err,addr,port,source_addr, | ||
| 471 | ANET_CONNECT_NONBLOCK|ANET_CONNECT_BE_BINDING); | ||
| 472 | } | ||
| 473 | |||
| 474 | int anetUnixGenericConnect(char *err, const char *path, int flags) | ||
| 475 | { | ||
| 476 | int s; | ||
| 477 | struct sockaddr_un sa; | ||
| 478 | |||
| 479 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) | ||
| 480 | return ANET_ERR; | ||
| 481 | |||
| 482 | sa.sun_family = AF_LOCAL; | ||
| 483 | redis_strlcpy(sa.sun_path,path,sizeof(sa.sun_path)); | ||
| 484 | if (flags & ANET_CONNECT_NONBLOCK) { | ||
| 485 | if (anetNonBlock(err,s) != ANET_OK) { | ||
| 486 | close(s); | ||
| 487 | return ANET_ERR; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { | ||
| 491 | if (errno == EINPROGRESS && | ||
| 492 | flags & ANET_CONNECT_NONBLOCK) | ||
| 493 | return s; | ||
| 494 | |||
| 495 | anetSetError(err, "connect: %s", strerror(errno)); | ||
| 496 | close(s); | ||
| 497 | return ANET_ERR; | ||
| 498 | } | ||
| 499 | return s; | ||
| 500 | } | ||
| 501 | |||
| 502 | static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog, mode_t perm) { | ||
| 503 | if (bind(s,sa,len) == -1) { | ||
| 504 | anetSetError(err, "bind: %s", strerror(errno)); | ||
| 505 | close(s); | ||
| 506 | return ANET_ERR; | ||
| 507 | } | ||
| 508 | |||
| 509 | if (sa->sa_family == AF_LOCAL && perm) | ||
| 510 | chmod(((struct sockaddr_un *) sa)->sun_path, perm); | ||
| 511 | |||
| 512 | if (listen(s, backlog) == -1) { | ||
| 513 | anetSetError(err, "listen: %s", strerror(errno)); | ||
| 514 | close(s); | ||
| 515 | return ANET_ERR; | ||
| 516 | } | ||
| 517 | return ANET_OK; | ||
| 518 | } | ||
| 519 | |||
| 520 | static int anetV6Only(char *err, int s) { | ||
| 521 | int yes = 1; | ||
| 522 | if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) { | ||
| 523 | anetSetError(err, "setsockopt: %s", strerror(errno)); | ||
| 524 | return ANET_ERR; | ||
| 525 | } | ||
| 526 | return ANET_OK; | ||
| 527 | } | ||
| 528 | |||
| 529 | static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog) | ||
| 530 | { | ||
| 531 | int s = -1, rv; | ||
| 532 | char _port[6]; /* strlen("65535") */ | ||
| 533 | struct addrinfo hints, *servinfo, *p; | ||
| 534 | |||
| 535 | snprintf(_port,6,"%d",port); | ||
| 536 | memset(&hints,0,sizeof(hints)); | ||
| 537 | hints.ai_family = af; | ||
| 538 | hints.ai_socktype = SOCK_STREAM; | ||
| 539 | hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */ | ||
| 540 | if (bindaddr && !strcmp("*", bindaddr)) | ||
| 541 | bindaddr = NULL; | ||
| 542 | if (af == AF_INET6 && bindaddr && !strcmp("::*", bindaddr)) | ||
| 543 | bindaddr = NULL; | ||
| 544 | |||
| 545 | if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) { | ||
| 546 | anetSetError(err, "%s", gai_strerror(rv)); | ||
| 547 | return ANET_ERR; | ||
| 548 | } | ||
| 549 | for (p = servinfo; p != NULL; p = p->ai_next) { | ||
| 550 | if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) | ||
| 551 | continue; | ||
| 552 | |||
| 553 | if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error; | ||
| 554 | if (anetSetReuseAddr(err,s) == ANET_ERR) goto error; | ||
| 555 | if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog,0) == ANET_ERR) s = ANET_ERR; | ||
| 556 | goto end; | ||
| 557 | } | ||
| 558 | if (p == NULL) { | ||
| 559 | anetSetError(err, "unable to bind socket, errno: %d", errno); | ||
| 560 | goto error; | ||
| 561 | } | ||
| 562 | |||
| 563 | error: | ||
| 564 | if (s != -1) close(s); | ||
| 565 | s = ANET_ERR; | ||
| 566 | end: | ||
| 567 | freeaddrinfo(servinfo); | ||
| 568 | return s; | ||
| 569 | } | ||
| 570 | |||
| 571 | int anetTcpServer(char *err, int port, char *bindaddr, int backlog) | ||
| 572 | { | ||
| 573 | return _anetTcpServer(err, port, bindaddr, AF_INET, backlog); | ||
| 574 | } | ||
| 575 | |||
| 576 | int anetTcp6Server(char *err, int port, char *bindaddr, int backlog) | ||
| 577 | { | ||
| 578 | return _anetTcpServer(err, port, bindaddr, AF_INET6, backlog); | ||
| 579 | } | ||
| 580 | |||
| 581 | int anetUnixServer(char *err, char *path, mode_t perm, int backlog) | ||
| 582 | { | ||
| 583 | int s; | ||
| 584 | struct sockaddr_un sa; | ||
| 585 | |||
| 586 | if (strlen(path) > sizeof(sa.sun_path)-1) { | ||
| 587 | anetSetError(err,"unix socket path too long (%zu), must be under %zu", strlen(path), sizeof(sa.sun_path)); | ||
| 588 | return ANET_ERR; | ||
| 589 | } | ||
| 590 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) | ||
| 591 | return ANET_ERR; | ||
| 592 | |||
| 593 | memset(&sa,0,sizeof(sa)); | ||
| 594 | sa.sun_family = AF_LOCAL; | ||
| 595 | redis_strlcpy(sa.sun_path,path,sizeof(sa.sun_path)); | ||
| 596 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog,perm) == ANET_ERR) | ||
| 597 | return ANET_ERR; | ||
| 598 | return s; | ||
| 599 | } | ||
| 600 | |||
| 601 | /* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC. | ||
| 602 | * returns the new socket FD, or -1 on error. */ | ||
| 603 | static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { | ||
| 604 | int fd; | ||
| 605 | do { | ||
| 606 | /* Use the accept4() call on linux to simultaneously accept and | ||
| 607 | * set a socket as non-blocking. */ | ||
| 608 | #ifdef HAVE_ACCEPT4 | ||
| 609 | fd = accept4(s, sa, len, SOCK_NONBLOCK | SOCK_CLOEXEC); | ||
| 610 | #else | ||
| 611 | fd = accept(s,sa,len); | ||
| 612 | #endif | ||
| 613 | } while(fd == -1 && errno == EINTR); | ||
| 614 | if (fd == -1) { | ||
| 615 | anetSetError(err, "accept: %s", strerror(errno)); | ||
| 616 | return ANET_ERR; | ||
| 617 | } | ||
| 618 | #ifndef HAVE_ACCEPT4 | ||
| 619 | if (anetCloexec(fd) == -1) { | ||
| 620 | anetSetError(err, "anetCloexec: %s", strerror(errno)); | ||
| 621 | close(fd); | ||
| 622 | return ANET_ERR; | ||
| 623 | } | ||
| 624 | if (anetNonBlock(err, fd) != ANET_OK) { | ||
| 625 | close(fd); | ||
| 626 | return ANET_ERR; | ||
| 627 | } | ||
| 628 | #endif | ||
| 629 | return fd; | ||
| 630 | } | ||
| 631 | |||
| 632 | /* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC. | ||
| 633 | * returns the new socket FD, or -1 on error. */ | ||
| 634 | int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port) { | ||
| 635 | int fd; | ||
| 636 | struct sockaddr_storage sa; | ||
| 637 | socklen_t salen = sizeof(sa); | ||
| 638 | if ((fd = anetGenericAccept(err,serversock,(struct sockaddr*)&sa,&salen)) == ANET_ERR) | ||
| 639 | return ANET_ERR; | ||
| 640 | |||
| 641 | if (sa.ss_family == AF_INET) { | ||
| 642 | struct sockaddr_in *s = (struct sockaddr_in *)&sa; | ||
| 643 | if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len); | ||
| 644 | if (port) *port = ntohs(s->sin_port); | ||
| 645 | } else { | ||
| 646 | struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa; | ||
| 647 | if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len); | ||
| 648 | if (port) *port = ntohs(s->sin6_port); | ||
| 649 | } | ||
| 650 | return fd; | ||
| 651 | } | ||
| 652 | |||
| 653 | /* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC. | ||
| 654 | * returns the new socket FD, or -1 on error. */ | ||
| 655 | int anetUnixAccept(char *err, int s) { | ||
| 656 | int fd; | ||
| 657 | struct sockaddr_un sa; | ||
| 658 | socklen_t salen = sizeof(sa); | ||
| 659 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) | ||
| 660 | return ANET_ERR; | ||
| 661 | |||
| 662 | return fd; | ||
| 663 | } | ||
| 664 | |||
| 665 | int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int remote) { | ||
| 666 | struct sockaddr_storage sa; | ||
| 667 | socklen_t salen = sizeof(sa); | ||
| 668 | |||
| 669 | if (remote) { | ||
| 670 | if (getpeername(fd, (struct sockaddr *)&sa, &salen) == -1) goto error; | ||
| 671 | } else { | ||
| 672 | if (getsockname(fd, (struct sockaddr *)&sa, &salen) == -1) goto error; | ||
| 673 | } | ||
| 674 | |||
| 675 | if (sa.ss_family == AF_INET) { | ||
| 676 | struct sockaddr_in *s = (struct sockaddr_in *)&sa; | ||
| 677 | if (ip) { | ||
| 678 | if (inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len) == NULL) | ||
| 679 | goto error; | ||
| 680 | } | ||
| 681 | if (port) *port = ntohs(s->sin_port); | ||
| 682 | } else if (sa.ss_family == AF_INET6) { | ||
| 683 | struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa; | ||
| 684 | if (ip) { | ||
| 685 | if (inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len) == NULL) | ||
| 686 | goto error; | ||
| 687 | } | ||
| 688 | if (port) *port = ntohs(s->sin6_port); | ||
| 689 | } else if (sa.ss_family == AF_UNIX) { | ||
| 690 | if (ip) { | ||
| 691 | int res = snprintf(ip, ip_len, "/unixsocket"); | ||
| 692 | if (res < 0 || (unsigned int) res >= ip_len) goto error; | ||
| 693 | } | ||
| 694 | if (port) *port = 0; | ||
| 695 | } else { | ||
| 696 | goto error; | ||
| 697 | } | ||
| 698 | return 0; | ||
| 699 | |||
| 700 | error: | ||
| 701 | if (ip) { | ||
| 702 | if (ip_len >= 2) { | ||
| 703 | ip[0] = '?'; | ||
| 704 | ip[1] = '\0'; | ||
| 705 | } else if (ip_len == 1) { | ||
| 706 | ip[0] = '\0'; | ||
| 707 | } | ||
| 708 | } | ||
| 709 | if (port) *port = 0; | ||
| 710 | return -1; | ||
| 711 | } | ||
| 712 | |||
| 713 | /* Create a pipe buffer with given flags for read end and write end. | ||
| 714 | * Note that it supports the file flags defined by pipe2() and fcntl(F_SETFL), | ||
| 715 | * and one of the use cases is O_CLOEXEC|O_NONBLOCK. */ | ||
| 716 | int anetPipe(int fds[2], int read_flags, int write_flags) { | ||
| 717 | int pipe_flags = 0; | ||
| 718 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 719 | /* When possible, try to leverage pipe2() to apply flags that are common to both ends. | ||
| 720 | * There is no harm to set O_CLOEXEC to prevent fd leaks. */ | ||
| 721 | pipe_flags = O_CLOEXEC | (read_flags & write_flags); | ||
| 722 | if (pipe2(fds, pipe_flags)) { | ||
| 723 | /* Fail on real failures, and fallback to simple pipe if pipe2 is unsupported. */ | ||
| 724 | if (errno != ENOSYS && errno != EINVAL) | ||
| 725 | return -1; | ||
| 726 | pipe_flags = 0; | ||
| 727 | } else { | ||
| 728 | /* If the flags on both ends are identical, no need to do anything else. */ | ||
| 729 | if ((O_CLOEXEC | read_flags) == (O_CLOEXEC | write_flags)) | ||
| 730 | return 0; | ||
| 731 | /* Clear the flags which have already been set using pipe2. */ | ||
| 732 | read_flags &= ~pipe_flags; | ||
| 733 | write_flags &= ~pipe_flags; | ||
| 734 | } | ||
| 735 | #endif | ||
| 736 | |||
| 737 | /* When we reach here with pipe_flags of 0, it means pipe2 failed (or was not attempted), | ||
| 738 | * so we try to use pipe. Otherwise, we skip and proceed to set specific flags below. */ | ||
| 739 | if (pipe_flags == 0 && pipe(fds)) | ||
| 740 | return -1; | ||
| 741 | |||
| 742 | /* File descriptor flags. | ||
| 743 | * Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. */ | ||
| 744 | if (read_flags & O_CLOEXEC) | ||
| 745 | if (fcntl(fds[0], F_SETFD, FD_CLOEXEC)) | ||
| 746 | goto error; | ||
| 747 | if (write_flags & O_CLOEXEC) | ||
| 748 | if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) | ||
| 749 | goto error; | ||
| 750 | |||
| 751 | /* File status flags after clearing the file descriptor flag O_CLOEXEC. */ | ||
| 752 | read_flags &= ~O_CLOEXEC; | ||
| 753 | if (read_flags) | ||
| 754 | if (fcntl(fds[0], F_SETFL, read_flags)) | ||
| 755 | goto error; | ||
| 756 | write_flags &= ~O_CLOEXEC; | ||
| 757 | if (write_flags) | ||
| 758 | if (fcntl(fds[1], F_SETFL, write_flags)) | ||
| 759 | goto error; | ||
| 760 | |||
| 761 | return 0; | ||
| 762 | |||
| 763 | error: | ||
| 764 | close(fds[0]); | ||
| 765 | close(fds[1]); | ||
| 766 | return -1; | ||
| 767 | } | ||
| 768 | |||
| 769 | int anetSetSockMarkId(char *err, int fd, uint32_t id) { | ||
| 770 | #ifdef HAVE_SOCKOPTMARKID | ||
| 771 | if (setsockopt(fd, SOL_SOCKET, SOCKOPTMARKID, (void *)&id, sizeof(id)) == -1) { | ||
| 772 | anetSetError(err, "setsockopt: %s", strerror(errno)); | ||
| 773 | return ANET_ERR; | ||
| 774 | } | ||
| 775 | return ANET_OK; | ||
| 776 | #else | ||
| 777 | UNUSED(fd); | ||
| 778 | UNUSED(id); | ||
| 779 | anetSetError(err,"anetSetSockMarkid unsupported on this platform"); | ||
| 780 | return ANET_OK; | ||
| 781 | #endif | ||
| 782 | } | ||
| 783 | |||
| 784 | int anetIsFifo(char *filepath) { | ||
| 785 | struct stat sb; | ||
| 786 | if (stat(filepath, &sb) == -1) return 0; | ||
| 787 | return S_ISFIFO(sb.st_mode); | ||
| 788 | } | ||
| 789 | |||
| 790 | /* This function must be called after accept4() fails. It returns 1 if 'err' | ||
| 791 | * indicates accepted connection faced an error, and it's okay to continue | ||
| 792 | * accepting next connection by calling accept4() again. Other errors either | ||
| 793 | * indicate programming errors, e.g. calling accept() on a closed fd or indicate | ||
| 794 | * a resource limit has been reached, e.g. -EMFILE, open fd limit has been | ||
| 795 | * reached. In the latter case, caller might wait until resources are available. | ||
| 796 | * See accept4() documentation for details. */ | ||
| 797 | int anetAcceptFailureNeedsRetry(int err) { | ||
| 798 | if (err == ECONNABORTED) | ||
| 799 | return 1; | ||
| 800 | |||
| 801 | #if defined(__linux__) | ||
| 802 | /* For details, see 'Error Handling' section on | ||
| 803 | * https://man7.org/linux/man-pages/man2/accept.2.html */ | ||
| 804 | if (err == ENETDOWN || err == EPROTO || err == ENOPROTOOPT || | ||
| 805 | err == EHOSTDOWN || err == ENONET || err == EHOSTUNREACH || | ||
| 806 | err == EOPNOTSUPP || err == ENETUNREACH) | ||
| 807 | { | ||
| 808 | return 1; | ||
| 809 | } | ||
| 810 | #endif | ||
| 811 | return 0; | ||
| 812 | } | ||
