2009-03-22 05:30:00 -04:00
|
|
|
/* anet.c -- Basic TCP socket stuff made a bit less boring
|
|
|
|
*
|
2012-11-08 12:25:23 -05:00
|
|
|
* Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
2009-03-22 05:30:00 -04:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Redis nor the names of its contributors may be used
|
|
|
|
* to endorse or promote products derived from this software without
|
|
|
|
* specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2009-03-27 17:00:27 -04:00
|
|
|
#include "fmacros.h"
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
2011-10-10 14:21:15 -04:00
|
|
|
#include <sys/stat.h>
|
2010-08-01 16:55:24 -04:00
|
|
|
#include <sys/un.h>
|
2014-10-22 09:23:21 -04:00
|
|
|
#include <sys/time.h>
|
2009-03-22 05:30:00 -04:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "anet.h"
|
2021-07-05 03:34:20 -04:00
|
|
|
#include "config.h"
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
static void anetSetError(char *err, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (!err) return;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(err, ANET_ERR_LEN, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2014-10-17 10:30:11 -04:00
|
|
|
int anetSetBlock(char *err, int fd, int non_block) {
|
2009-03-22 05:30:00 -04:00
|
|
|
int flags;
|
|
|
|
|
2014-10-17 10:30:11 -04:00
|
|
|
/* Set the socket blocking (if non_block is zero) or non-blocking.
|
2009-03-22 05:30:00 -04:00
|
|
|
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
|
|
|
* interrupted by a signal. */
|
|
|
|
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
2011-01-05 04:50:47 -05:00
|
|
|
anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno));
|
2009-03-22 05:30:00 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
}
|
2014-10-17 10:30:11 -04:00
|
|
|
|
2021-01-19 04:36:21 -05:00
|
|
|
/* Check if this flag has been set or unset, if so,
|
|
|
|
* then there is no need to call fcntl to set/unset it again. */
|
|
|
|
if (!!(flags & O_NONBLOCK) == !!non_block)
|
|
|
|
return ANET_OK;
|
|
|
|
|
2014-10-17 10:30:11 -04:00
|
|
|
if (non_block)
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
else
|
|
|
|
flags &= ~O_NONBLOCK;
|
|
|
|
|
|
|
|
if (fcntl(fd, F_SETFL, flags) == -1) {
|
2011-01-05 04:50:47 -05:00
|
|
|
anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno));
|
2009-03-22 05:30:00 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-17 10:30:11 -04:00
|
|
|
int anetNonBlock(char *err, int fd) {
|
|
|
|
return anetSetBlock(err,fd,1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int anetBlock(char *err, int fd) {
|
|
|
|
return anetSetBlock(err,fd,0);
|
|
|
|
}
|
|
|
|
|
2021-01-19 15:57:30 -05:00
|
|
|
/* Enable the FD_CLOEXEC on the given fd to avoid fd leaks.
|
|
|
|
* This function should be invoked for fd's on specific places
|
|
|
|
* where fork + execve system calls are called. */
|
|
|
|
int anetCloexec(int fd) {
|
|
|
|
int r;
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
do {
|
|
|
|
r = fcntl(fd, F_GETFD);
|
|
|
|
} while (r == -1 && errno == EINTR);
|
|
|
|
|
|
|
|
if (r == -1 || (r & FD_CLOEXEC))
|
|
|
|
return r;
|
|
|
|
|
|
|
|
flags = r | FD_CLOEXEC;
|
|
|
|
|
|
|
|
do {
|
|
|
|
r = fcntl(fd, F_SETFD, flags);
|
|
|
|
} while (r == -1 && errno == EINTR);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-02-08 10:30:21 -05:00
|
|
|
/* Set TCP keep alive option to detect dead peers. The interval option
|
|
|
|
* is only used for Linux as we are using Linux-specific APIs to set
|
|
|
|
* the probe send time, interval, and count. */
|
|
|
|
int anetKeepAlive(char *err, int fd, int interval)
|
|
|
|
{
|
|
|
|
int val = 1;
|
|
|
|
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)
|
|
|
|
{
|
|
|
|
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
/* Default settings are more or less garbage, with the keepalive time
|
|
|
|
* set to 7200 by default on Linux. Modify settings to make the feature
|
|
|
|
* actually useful. */
|
|
|
|
|
|
|
|
/* Send first probe after interval. */
|
|
|
|
val = interval;
|
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
|
|
|
anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
|
2013-02-08 11:06:01 -05:00
|
|
|
/* Send next probes after the specified interval. Note that we set the
|
|
|
|
* delay as interval / 3, as we send three probes before detecting
|
|
|
|
* an error (see the next setsockopt call). */
|
|
|
|
val = interval/3;
|
|
|
|
if (val == 0) val = 1;
|
2013-02-08 10:30:21 -05:00
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
|
|
|
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
|
2013-02-08 11:06:01 -05:00
|
|
|
/* Consider the socket in error state after three we send three ACK
|
|
|
|
* probes without getting a reply. */
|
|
|
|
val = 3;
|
2013-02-08 10:30:21 -05:00
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
|
|
|
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
2014-08-13 05:44:38 -04:00
|
|
|
#else
|
|
|
|
((void) interval); /* Avoid unused var warning for non Linux systems. */
|
2013-02-08 10:30:21 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2013-01-31 05:14:15 -05:00
|
|
|
static int anetSetTcpNoDelay(char *err, int fd, int val)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2013-01-31 05:14:15 -05:00
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2011-01-05 04:50:47 -05:00
|
|
|
anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
|
2009-03-22 05:30:00 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2013-01-31 05:14:15 -05:00
|
|
|
int anetEnableTcpNoDelay(char *err, int fd)
|
2013-01-30 22:09:16 -05:00
|
|
|
{
|
2013-01-31 05:14:15 -05:00
|
|
|
return anetSetTcpNoDelay(err, fd, 1);
|
2013-01-30 22:09:16 -05:00
|
|
|
}
|
|
|
|
|
2014-06-26 12:48:40 -04:00
|
|
|
int anetDisableTcpNoDelay(char *err, int fd)
|
2013-01-30 22:09:16 -05:00
|
|
|
{
|
2013-01-31 05:14:15 -05:00
|
|
|
return anetSetTcpNoDelay(err, fd, 0);
|
2013-01-30 22:09:16 -05:00
|
|
|
}
|
|
|
|
|
2014-10-22 09:23:21 -04:00
|
|
|
/* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified
|
|
|
|
* number of milliseconds, or disable it if the 'ms' argument is zero. */
|
|
|
|
int anetSendTimeout(char *err, int fd, long long ms) {
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
tv.tv_sec = ms/1000;
|
|
|
|
tv.tv_usec = (ms%1000)*1000;
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
|
|
|
|
anetSetError(err, "setsockopt SO_SNDTIMEO: %s", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2019-07-01 08:22:29 -04:00
|
|
|
/* Set the socket receive timeout (SO_RCVTIMEO socket option) to the specified
|
|
|
|
* number of milliseconds, or disable it if the 'ms' argument is zero. */
|
|
|
|
int anetRecvTimeout(char *err, int fd, long long ms) {
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
tv.tv_sec = ms/1000;
|
|
|
|
tv.tv_usec = (ms%1000)*1000;
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
|
|
|
|
anetSetError(err, "setsockopt SO_RCVTIMEO: %s", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
Add hostname support in Sentinel. (#8282)
This is both a bugfix and an enhancement.
Internally, Sentinel relies entirely on IP addresses to identify
instances. When configured with a new master, it also requires users to
specify and IP and not hostname.
However, replicas may use the replica-announce-ip configuration to
announce a hostname. When that happens, Sentinel fails to match the
announced hostname with the expected IP and considers that a different
instance, triggering reconfiguration, etc.
Another use case is where TLS is used and clients are expected to match
the hostname to connect to with the certificate's SAN attribute. To
properly implement this configuration, it is necessary for Sentinel to
redirect clients to a hostname rather than an IP address.
The new 'resolve-hostnames' configuration parameter determines if
Sentinel is willing to accept hostnames. It is set by default to no,
which maintains backwards compatibility and avoids unexpected DNS
resolution delays on systems with DNS configuration issues.
Internally, Sentinel continues to identify instances by their resolved
IP address and will also report the IP by default. The new
'announce-hostnames' parameter determines if Sentinel should prefer to
announce a hostname, when available, rather than an IP address. This
applies to addresses returned to clients, as well as their
representation in the configuration file, REPLICAOF configuration
commands, etc.
This commit also introduces SENTINEL CONFIG GET and SENTINEL CONFIG SET
which can be used to introspect or configure global Sentinel
configuration that was previously was only possible by directly
accessing the configuration file and possibly restarting the instance.
Co-authored-by: myl1024 <myl92916@qq.com>
Co-authored-by: sundb <sundbcn@gmail.com>
2021-01-28 05:09:11 -05:00
|
|
|
/* Resolve the hostname "host" and set the string representation of the
|
|
|
|
* IP address into the buffer pointed by "ipbuf".
|
2014-01-10 09:02:39 -05:00
|
|
|
*
|
|
|
|
* If flags is set to ANET_IP_ONLY the function only resolves hostnames
|
|
|
|
* that are actually already IPv4 or IPv6 addresses. This turns the function
|
|
|
|
* into a validating / normalizing function. */
|
Add hostname support in Sentinel. (#8282)
This is both a bugfix and an enhancement.
Internally, Sentinel relies entirely on IP addresses to identify
instances. When configured with a new master, it also requires users to
specify and IP and not hostname.
However, replicas may use the replica-announce-ip configuration to
announce a hostname. When that happens, Sentinel fails to match the
announced hostname with the expected IP and considers that a different
instance, triggering reconfiguration, etc.
Another use case is where TLS is used and clients are expected to match
the hostname to connect to with the certificate's SAN attribute. To
properly implement this configuration, it is necessary for Sentinel to
redirect clients to a hostname rather than an IP address.
The new 'resolve-hostnames' configuration parameter determines if
Sentinel is willing to accept hostnames. It is set by default to no,
which maintains backwards compatibility and avoids unexpected DNS
resolution delays on systems with DNS configuration issues.
Internally, Sentinel continues to identify instances by their resolved
IP address and will also report the IP by default. The new
'announce-hostnames' parameter determines if Sentinel should prefer to
announce a hostname, when available, rather than an IP address. This
applies to addresses returned to clients, as well as their
representation in the configuration file, REPLICAOF configuration
commands, etc.
This commit also introduces SENTINEL CONFIG GET and SENTINEL CONFIG SET
which can be used to introspect or configure global Sentinel
configuration that was previously was only possible by directly
accessing the configuration file and possibly restarting the instance.
Co-authored-by: myl1024 <myl92916@qq.com>
Co-authored-by: sundb <sundbcn@gmail.com>
2021-01-28 05:09:11 -05:00
|
|
|
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,
|
2014-01-10 09:02:39 -05:00
|
|
|
int flags)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2011-06-16 19:29:49 -04:00
|
|
|
struct addrinfo hints, *info;
|
|
|
|
int rv;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2011-06-16 19:29:49 -04:00
|
|
|
memset(&hints,0,sizeof(hints));
|
2014-01-10 09:02:39 -05:00
|
|
|
if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;
|
2011-06-18 08:25:31 -04:00
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_socktype = SOCK_STREAM; /* specify socktype to avoid dups */
|
2011-06-16 19:29:49 -04:00
|
|
|
|
|
|
|
if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) {
|
|
|
|
anetSetError(err, "%s", gai_strerror(rv));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
if (info->ai_family == AF_INET) {
|
|
|
|
struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr;
|
2011-06-18 08:25:31 -04:00
|
|
|
inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len);
|
|
|
|
} else {
|
|
|
|
struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr;
|
|
|
|
inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2011-06-16 19:29:49 -04:00
|
|
|
|
|
|
|
freeaddrinfo(info);
|
2009-03-22 05:30:00 -04:00
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2011-06-16 19:55:00 -04:00
|
|
|
static int anetSetReuseAddr(char *err, int fd) {
|
|
|
|
int yes = 1;
|
2017-08-08 05:45:51 -04:00
|
|
|
/* Make sure connection-intensive things like the redis benchmark
|
2011-06-16 19:55:00 -04:00
|
|
|
* will be able to close/open sockets a zillion of times */
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
|
|
|
|
anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2010-10-13 10:33:27 -04:00
|
|
|
static int anetCreateSocket(char *err, int domain) {
|
2011-06-16 19:55:00 -04:00
|
|
|
int s;
|
2010-10-13 10:33:27 -04:00
|
|
|
if ((s = socket(domain, SOCK_STREAM, 0)) == -1) {
|
2011-01-05 04:50:47 -05:00
|
|
|
anetSetError(err, "creating socket: %s", strerror(errno));
|
2010-10-13 10:33:27 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
|
2013-01-16 12:00:20 -05:00
|
|
|
/* Make sure connection-intensive things like the redis benchmark
|
2010-10-13 10:33:27 -04:00
|
|
|
* will be able to close/open sockets a zillion of times */
|
2011-06-16 19:55:00 -04:00
|
|
|
if (anetSetReuseAddr(err,s) == ANET_ERR) {
|
|
|
|
close(s);
|
2010-10-13 10:33:27 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
#define ANET_CONNECT_NONE 0
|
|
|
|
#define ANET_CONNECT_NONBLOCK 1
|
2015-06-11 06:55:58 -04:00
|
|
|
#define ANET_CONNECT_BE_BINDING 2 /* Best effort binding. */
|
2019-09-12 03:56:54 -04:00
|
|
|
static int anetTcpGenericConnect(char *err, const char *addr, int port,
|
|
|
|
const char *source_addr, int flags)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2013-12-25 12:13:27 -05:00
|
|
|
int s = ANET_ERR, rv;
|
2013-12-25 12:28:30 -05:00
|
|
|
char portstr[6]; /* strlen("65535") + 1; */
|
2014-03-03 10:57:27 -05:00
|
|
|
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
|
2011-06-16 20:06:19 -04:00
|
|
|
|
2013-12-25 12:28:30 -05:00
|
|
|
snprintf(portstr,sizeof(portstr),"%d",port);
|
2011-06-16 20:06:19 -04:00
|
|
|
memset(&hints,0,sizeof(hints));
|
2011-06-18 14:54:05 -04:00
|
|
|
hints.ai_family = AF_UNSPEC;
|
2011-06-16 20:06:19 -04:00
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2013-12-25 12:28:30 -05:00
|
|
|
if ((rv = getaddrinfo(addr,portstr,&hints,&servinfo)) != 0) {
|
2011-06-16 20:06:19 -04:00
|
|
|
anetSetError(err, "%s", gai_strerror(rv));
|
2009-03-22 05:30:00 -04:00
|
|
|
return ANET_ERR;
|
2011-06-16 20:06:19 -04:00
|
|
|
}
|
|
|
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
2013-12-25 12:13:27 -05:00
|
|
|
/* Try to create the socket and to connect it.
|
|
|
|
* If we fail in the socket() call, or on connect(), we retry with
|
|
|
|
* the next entry in servinfo. */
|
2011-06-16 20:06:19 -04:00
|
|
|
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
|
|
|
continue;
|
2013-07-10 08:37:13 -04:00
|
|
|
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
|
|
|
if (flags & ANET_CONNECT_NONBLOCK && anetNonBlock(err,s) != ANET_OK)
|
2011-06-16 20:06:19 -04:00
|
|
|
goto error;
|
2014-03-03 10:57:27 -05:00
|
|
|
if (source_addr) {
|
|
|
|
int bound = 0;
|
|
|
|
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
|
2014-06-09 14:32:27 -04:00
|
|
|
if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0)
|
|
|
|
{
|
2014-03-03 10:57:27 -05:00
|
|
|
anetSetError(err, "%s", gai_strerror(rv));
|
2015-06-11 06:46:55 -04:00
|
|
|
goto error;
|
2014-03-03 10:57:27 -05:00
|
|
|
}
|
|
|
|
for (b = bservinfo; b != NULL; b = b->ai_next) {
|
|
|
|
if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
|
|
|
|
bound = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-06-09 14:32:27 -04:00
|
|
|
freeaddrinfo(bservinfo);
|
2014-03-03 10:57:27 -05:00
|
|
|
if (!bound) {
|
|
|
|
anetSetError(err, "bind: %s", strerror(errno));
|
2015-06-11 06:46:55 -04:00
|
|
|
goto error;
|
2014-03-03 10:57:27 -05:00
|
|
|
}
|
|
|
|
}
|
2011-06-16 20:06:19 -04:00
|
|
|
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
2013-12-25 12:13:27 -05:00
|
|
|
/* If the socket is non-blocking, it is ok for connect() to
|
|
|
|
* return an EINPROGRESS error here. */
|
|
|
|
if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK)
|
|
|
|
goto end;
|
2009-03-22 05:30:00 -04:00
|
|
|
close(s);
|
2013-12-25 12:13:27 -05:00
|
|
|
s = ANET_ERR;
|
2011-06-16 20:06:19 -04:00
|
|
|
continue;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2011-06-16 20:06:19 -04:00
|
|
|
|
2013-12-25 12:13:27 -05:00
|
|
|
/* If we ended an iteration of the for loop without errors, we
|
|
|
|
* have a connected socket. Let's return to the caller. */
|
2011-06-16 20:06:19 -04:00
|
|
|
goto end;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2013-12-25 12:24:04 -05:00
|
|
|
if (p == NULL)
|
2011-06-16 20:06:19 -04:00
|
|
|
anetSetError(err, "creating socket: %s", strerror(errno));
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2011-06-16 20:06:19 -04:00
|
|
|
error:
|
2013-12-25 12:13:27 -05:00
|
|
|
if (s != ANET_ERR) {
|
|
|
|
close(s);
|
|
|
|
s = ANET_ERR;
|
|
|
|
}
|
2015-06-11 06:55:58 -04:00
|
|
|
|
2011-06-16 20:06:19 -04:00
|
|
|
end:
|
|
|
|
freeaddrinfo(servinfo);
|
2015-06-11 06:55:58 -04:00
|
|
|
|
|
|
|
/* Handle best effort binding: if a binding address was used, but it is
|
|
|
|
* not possible to create a socket, try again without a binding address. */
|
|
|
|
if (s == ANET_ERR && source_addr && (flags & ANET_CONNECT_BE_BINDING)) {
|
|
|
|
return anetTcpGenericConnect(err,addr,port,NULL,flags);
|
|
|
|
} else {
|
|
|
|
return s;
|
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2019-09-12 03:56:54 -04:00
|
|
|
int anetTcpNonBlockConnect(char *err, const char *addr, int port)
|
2009-03-22 05:30:00 -04:00
|
|
|
{
|
2014-03-03 10:57:27 -05:00
|
|
|
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);
|
|
|
|
}
|
|
|
|
|
2019-09-12 03:56:54 -04:00
|
|
|
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,
|
|
|
|
const char *source_addr)
|
2014-03-03 10:57:27 -05:00
|
|
|
{
|
2015-06-11 06:55:58 -04:00
|
|
|
return anetTcpGenericConnect(err,addr,port,source_addr,
|
|
|
|
ANET_CONNECT_NONBLOCK|ANET_CONNECT_BE_BINDING);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2019-09-12 03:56:54 -04:00
|
|
|
int anetUnixGenericConnect(char *err, const char *path, int flags)
|
2010-08-01 16:55:24 -04:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
struct sockaddr_un sa;
|
|
|
|
|
2010-10-13 10:33:27 -04:00
|
|
|
if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)
|
2010-08-01 16:55:24 -04:00
|
|
|
return ANET_ERR;
|
2010-10-13 10:33:27 -04:00
|
|
|
|
2010-08-01 16:55:24 -04:00
|
|
|
sa.sun_family = AF_LOCAL;
|
|
|
|
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
|
|
|
if (flags & ANET_CONNECT_NONBLOCK) {
|
2017-04-18 10:14:33 -04:00
|
|
|
if (anetNonBlock(err,s) != ANET_OK) {
|
|
|
|
close(s);
|
2010-08-01 16:55:24 -04:00
|
|
|
return ANET_ERR;
|
2017-04-18 10:14:33 -04:00
|
|
|
}
|
2010-08-01 16:55:24 -04:00
|
|
|
}
|
|
|
|
if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) {
|
|
|
|
if (errno == EINPROGRESS &&
|
|
|
|
flags & ANET_CONNECT_NONBLOCK)
|
|
|
|
return s;
|
|
|
|
|
2011-01-05 04:50:47 -05:00
|
|
|
anetSetError(err, "connect: %s", strerror(errno));
|
2010-08-01 16:55:24 -04:00
|
|
|
close(s);
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2013-11-08 14:55:48 -05:00
|
|
|
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog) {
|
2010-10-13 10:33:27 -04:00
|
|
|
if (bind(s,sa,len) == -1) {
|
2011-01-05 04:50:47 -05:00
|
|
|
anetSetError(err, "bind: %s", strerror(errno));
|
2010-10-13 10:33:27 -04:00
|
|
|
close(s);
|
2009-03-22 05:30:00 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
}
|
2012-04-11 11:04:31 -04:00
|
|
|
|
2013-11-08 14:55:48 -05:00
|
|
|
if (listen(s, backlog) == -1) {
|
2011-01-05 04:50:47 -05:00
|
|
|
anetSetError(err, "listen: %s", strerror(errno));
|
2009-03-22 05:30:00 -04:00
|
|
|
close(s);
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
2010-10-13 10:33:27 -04:00
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2011-09-19 18:31:41 -04:00
|
|
|
static int anetV6Only(char *err, int s) {
|
|
|
|
int yes = 1;
|
|
|
|
if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) {
|
|
|
|
anetSetError(err, "setsockopt: %s", strerror(errno));
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
return ANET_OK;
|
|
|
|
}
|
|
|
|
|
2013-11-08 14:55:48 -05:00
|
|
|
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog)
|
2010-10-13 10:33:27 -04:00
|
|
|
{
|
2017-04-18 10:24:06 -04:00
|
|
|
int s = -1, rv;
|
2011-06-16 20:49:21 -04:00
|
|
|
char _port[6]; /* strlen("65535") */
|
|
|
|
struct addrinfo hints, *servinfo, *p;
|
2010-10-13 10:33:27 -04:00
|
|
|
|
2011-06-16 20:49:21 -04:00
|
|
|
snprintf(_port,6,"%d",port);
|
|
|
|
memset(&hints,0,sizeof(hints));
|
2011-09-19 18:32:41 -04:00
|
|
|
hints.ai_family = af;
|
2011-06-16 20:49:21 -04:00
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */
|
Optionally (default) fail to start if requested bind address is not available (#7936)
Background:
#3467 (redis 4.0.0), started ignoring ENOPROTOOPT, but did that only for
the default bind (in case bind config wasn't explicitly set).
#5598 (redis 5.0.3), added that for bind addresses explicitly set
(following bug reports in Debian for redis 4.0.9 and 5.0.1), it
also ignored a bunch of other errors like EPROTONOSUPPORT which was
requested in #3894, and also added EADDRNOTAVAIL (wasn't clear why).
This (ignoring EADDRNOTAVAIL) makes redis start successfully, even if a
certain network interface isn't up yet , in which case we rather redis
fail and will be re-tried when the NIC is up, see #7933.
However, it turns out that when IPv6 is disabled (supported but unused),
the error we're getting is EADDRNOTAVAIL. and in many systems the
default config file tries to bind to localhost for both v4 and v6 and
would like to silently ignore the error on v6 if disabled.
This means that we sometimes want to ignore EADDRNOTAVAIL and other times
we wanna fail.
So this commit changes these main things:
1. Ignore all the errors we ignore for both explicitly requested bind
address and a default implicit one.
2. Add a '-' prefix to allow EADDRNOTAVAIL be ignored (by default that's
different than the previous behavior).
3. Restructure that function in a more readable and maintainable way see
below.
4. Make the default behavior of listening to all achievable by setting
a bind config directive to * (previously only possible by omitting
it)
5. document everything.
The old structure of this function was that even if there are no bind
addresses requested, the loop that runs though the bind addresses runs
at least once anyway!
In that one iteration of the loop it binds to both v4 and v6 addresses,
handles errors for each of them separately, and then eventually at the
if-else chain, handles the error of the last bind attempt again!
This was very hard to read and very error prone to maintain, instead now
when the bind info is missing we create one with two entries, and run
the simple loop twice.
2020-10-28 15:09:15 -04:00
|
|
|
if (bindaddr && !strcmp("*", bindaddr))
|
|
|
|
bindaddr = NULL;
|
|
|
|
if (af == AF_INET6 && bindaddr && !strcmp("::*", bindaddr))
|
|
|
|
bindaddr = NULL;
|
2010-10-13 10:33:27 -04:00
|
|
|
|
2011-06-16 20:49:21 -04:00
|
|
|
if ((rv = getaddrinfo(bindaddr,_port,&hints,&servinfo)) != 0) {
|
|
|
|
anetSetError(err, "%s", gai_strerror(rv));
|
2013-07-05 05:07:55 -04:00
|
|
|
return ANET_ERR;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2011-06-16 20:49:21 -04:00
|
|
|
for (p = servinfo; p != NULL; p = p->ai_next) {
|
|
|
|
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
|
|
|
continue;
|
|
|
|
|
2013-07-10 08:34:58 -04:00
|
|
|
if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
|
|
|
|
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
2018-03-21 03:34:13 -04:00
|
|
|
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) s = ANET_ERR;
|
2011-06-16 20:49:21 -04:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
if (p == NULL) {
|
2016-08-21 13:39:15 -04:00
|
|
|
anetSetError(err, "unable to bind socket, errno: %d", errno);
|
2011-06-16 20:49:21 -04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
2017-04-18 10:24:06 -04:00
|
|
|
if (s != -1) close(s);
|
2011-06-16 20:49:21 -04:00
|
|
|
s = ANET_ERR;
|
|
|
|
end:
|
|
|
|
freeaddrinfo(servinfo);
|
2009-03-22 05:30:00 -04:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2013-11-08 14:55:48 -05:00
|
|
|
int anetTcpServer(char *err, int port, char *bindaddr, int backlog)
|
2011-09-19 18:32:41 -04:00
|
|
|
{
|
2013-11-08 14:55:48 -05:00
|
|
|
return _anetTcpServer(err, port, bindaddr, AF_INET, backlog);
|
2011-09-19 18:32:41 -04:00
|
|
|
}
|
|
|
|
|
2013-11-08 14:55:48 -05:00
|
|
|
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog)
|
2011-09-19 18:32:41 -04:00
|
|
|
{
|
2013-11-08 14:55:48 -05:00
|
|
|
return _anetTcpServer(err, port, bindaddr, AF_INET6, backlog);
|
2011-09-19 18:32:41 -04:00
|
|
|
}
|
|
|
|
|
2013-11-08 14:55:48 -05:00
|
|
|
int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
|
2010-08-01 16:55:24 -04:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
struct sockaddr_un sa;
|
|
|
|
|
2010-10-13 10:33:27 -04:00
|
|
|
if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)
|
2010-08-01 16:55:24 -04:00
|
|
|
return ANET_ERR;
|
2010-10-13 10:33:27 -04:00
|
|
|
|
2010-08-01 16:55:24 -04:00
|
|
|
memset(&sa,0,sizeof(sa));
|
|
|
|
sa.sun_family = AF_LOCAL;
|
|
|
|
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
2013-11-08 14:55:48 -05:00
|
|
|
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
|
2010-08-01 16:55:24 -04:00
|
|
|
return ANET_ERR;
|
2011-10-10 14:21:15 -04:00
|
|
|
if (perm)
|
|
|
|
chmod(sa.sun_path, perm);
|
2010-08-01 16:55:24 -04:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2021-07-05 03:34:20 -04:00
|
|
|
/* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC.
|
|
|
|
* returns the new socket FD, or -1 on error. */
|
2010-10-13 12:34:24 -04:00
|
|
|
static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) {
|
2009-03-22 05:30:00 -04:00
|
|
|
int fd;
|
2021-04-21 02:04:36 -04:00
|
|
|
do {
|
2021-07-05 03:34:20 -04:00
|
|
|
/* Use the accept4() call on linux to simultaneously accept and
|
|
|
|
* set a socket as non-blocking. */
|
|
|
|
#ifdef HAVE_ACCEPT4
|
|
|
|
fd = accept4(s, sa, len, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
|
|
|
#else
|
2010-10-13 12:34:24 -04:00
|
|
|
fd = accept(s,sa,len);
|
2021-07-05 03:34:20 -04:00
|
|
|
#endif
|
2021-04-21 02:04:36 -04:00
|
|
|
} while(fd == -1 && errno == EINTR);
|
|
|
|
if (fd == -1) {
|
|
|
|
anetSetError(err, "accept: %s", strerror(errno));
|
|
|
|
return ANET_ERR;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2021-07-05 03:34:20 -04:00
|
|
|
#ifndef HAVE_ACCEPT4
|
|
|
|
if (anetCloexec(fd) == -1) {
|
|
|
|
anetSetError(err, "anetCloexec: %s", strerror(errno));
|
|
|
|
close(fd);
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
if (anetNonBlock(err, fd) != ANET_OK) {
|
|
|
|
close(fd);
|
|
|
|
return ANET_ERR;
|
|
|
|
}
|
|
|
|
#endif
|
2010-10-13 12:34:24 -04:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2021-07-05 03:34:20 -04:00
|
|
|
/* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC.
|
|
|
|
* returns the new socket FD, or -1 on error. */
|
2011-06-17 14:47:49 -04:00
|
|
|
int anetTcpAccept(char *err, int s, char *ip, size_t ip_len, int *port) {
|
2010-10-13 12:34:24 -04:00
|
|
|
int fd;
|
2011-06-18 09:47:38 -04:00
|
|
|
struct sockaddr_storage sa;
|
2010-10-13 12:34:24 -04:00
|
|
|
socklen_t salen = sizeof(sa);
|
2021-04-21 06:33:18 -04:00
|
|
|
if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR)
|
2010-10-13 12:34:24 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
|
2011-06-18 09:47:38 -04:00
|
|
|
if (sa.ss_family == AF_INET) {
|
|
|
|
struct sockaddr_in *s = (struct sockaddr_in *)&sa;
|
|
|
|
if (ip) inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len);
|
|
|
|
if (port) *port = ntohs(s->sin_port);
|
|
|
|
} else {
|
|
|
|
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
|
|
|
|
if (ip) inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len);
|
|
|
|
if (port) *port = ntohs(s->sin6_port);
|
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
return fd;
|
|
|
|
}
|
2010-10-13 12:34:24 -04:00
|
|
|
|
2021-07-05 03:34:20 -04:00
|
|
|
/* Accept a connection and also make sure the socket is non-blocking, and CLOEXEC.
|
|
|
|
* returns the new socket FD, or -1 on error. */
|
2010-10-13 12:50:07 -04:00
|
|
|
int anetUnixAccept(char *err, int s) {
|
2010-10-13 12:34:24 -04:00
|
|
|
int fd;
|
|
|
|
struct sockaddr_un sa;
|
|
|
|
socklen_t salen = sizeof(sa);
|
2021-04-21 06:33:18 -04:00
|
|
|
if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR)
|
2010-10-13 12:34:24 -04:00
|
|
|
return ANET_ERR;
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
2011-04-21 09:38:02 -04:00
|
|
|
|
2020-10-28 15:13:44 -04:00
|
|
|
int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_type) {
|
2011-06-18 11:20:08 -04:00
|
|
|
struct sockaddr_storage sa;
|
2011-04-21 09:38:02 -04:00
|
|
|
socklen_t salen = sizeof(sa);
|
|
|
|
|
2020-10-28 15:13:44 -04:00
|
|
|
if (fd_to_str_type == FD_TO_PEER_NAME) {
|
|
|
|
if (getpeername(fd, (struct sockaddr *)&sa, &salen) == -1) goto error;
|
|
|
|
} else {
|
|
|
|
if (getsockname(fd, (struct sockaddr *)&sa, &salen) == -1) goto error;
|
|
|
|
}
|
2014-09-18 11:22:19 -04:00
|
|
|
|
2011-06-18 11:20:08 -04:00
|
|
|
if (sa.ss_family == AF_INET) {
|
|
|
|
struct sockaddr_in *s = (struct sockaddr_in *)&sa;
|
2021-05-11 07:27:08 -04:00
|
|
|
if (ip) {
|
|
|
|
if (inet_ntop(AF_INET,(void*)&(s->sin_addr),ip,ip_len) == NULL)
|
|
|
|
goto error;
|
|
|
|
}
|
2011-06-18 11:20:08 -04:00
|
|
|
if (port) *port = ntohs(s->sin_port);
|
2014-09-18 10:07:29 -04:00
|
|
|
} else if (sa.ss_family == AF_INET6) {
|
2011-06-18 11:20:08 -04:00
|
|
|
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
|
2021-05-11 07:27:08 -04:00
|
|
|
if (ip) {
|
|
|
|
if (inet_ntop(AF_INET6,(void*)&(s->sin6_addr),ip,ip_len) == NULL)
|
|
|
|
goto error;
|
|
|
|
}
|
2011-06-18 11:20:08 -04:00
|
|
|
if (port) *port = ntohs(s->sin6_port);
|
2014-09-18 11:22:19 -04:00
|
|
|
} else if (sa.ss_family == AF_UNIX) {
|
2021-05-11 07:27:08 -04:00
|
|
|
if (ip) {
|
|
|
|
int res = snprintf(ip, ip_len, "/unixsocket");
|
|
|
|
if (res < 0 || (unsigned int) res >= ip_len) goto error;
|
|
|
|
}
|
2014-09-18 10:07:29 -04:00
|
|
|
if (port) *port = 0;
|
2014-09-18 11:22:19 -04:00
|
|
|
} else {
|
|
|
|
goto error;
|
2011-06-18 11:20:08 -04:00
|
|
|
}
|
2011-04-21 09:38:02 -04:00
|
|
|
return 0;
|
2014-09-18 11:22:19 -04:00
|
|
|
|
|
|
|
error:
|
|
|
|
if (ip) {
|
|
|
|
if (ip_len >= 2) {
|
|
|
|
ip[0] = '?';
|
|
|
|
ip[1] = '\0';
|
|
|
|
} else if (ip_len == 1) {
|
|
|
|
ip[0] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (port) *port = 0;
|
|
|
|
return -1;
|
2011-04-21 09:38:02 -04:00
|
|
|
}
|
2012-07-23 06:54:52 -04:00
|
|
|
|
2014-12-11 12:20:30 -05:00
|
|
|
/* Format an IP,port pair into something easy to parse. If IP is IPv6
|
|
|
|
* (matches for ":"), the ip is surrounded by []. IP and port are just
|
|
|
|
* separated by colons. This the standard to display addresses within Redis. */
|
|
|
|
int anetFormatAddr(char *buf, size_t buf_len, char *ip, int port) {
|
|
|
|
return snprintf(buf,buf_len, strchr(ip,':') ?
|
|
|
|
"[%s]:%d" : "%s:%d", ip, port);
|
2014-10-23 12:40:02 -04:00
|
|
|
}
|
|
|
|
|
2020-10-28 15:13:44 -04:00
|
|
|
/* Like anetFormatAddr() but extract ip and port from the socket's peer/sockname. */
|
|
|
|
int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type) {
|
2014-10-23 12:40:02 -04:00
|
|
|
char ip[INET6_ADDRSTRLEN];
|
|
|
|
int port;
|
|
|
|
|
2020-10-28 15:13:44 -04:00
|
|
|
anetFdToString(fd,ip,sizeof(ip),&port,fd_to_str_type);
|
2014-12-11 12:20:30 -05:00
|
|
|
return anetFormatAddr(buf, buf_len, ip, port);
|
2014-10-23 12:40:02 -04:00
|
|
|
}
|
2021-10-06 09:08:13 -04:00
|
|
|
|
|
|
|
/* Create a pipe buffer with given flags for read end and write end.
|
|
|
|
* Note that it supports the file flags defined by pipe2() and fcntl(F_SETFL),
|
|
|
|
* and one of the use cases is O_CLOEXEC|O_NONBLOCK. */
|
|
|
|
int anetPipe(int fds[2], int read_flags, int write_flags) {
|
|
|
|
int pipe_flags = 0;
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
|
|
|
/* When possible, try to leverage pipe2() to apply flags that are common to both ends.
|
|
|
|
* There is no harm to set O_CLOEXEC to prevent fd leaks. */
|
|
|
|
pipe_flags = O_CLOEXEC | (read_flags & write_flags);
|
|
|
|
if (pipe2(fds, pipe_flags)) {
|
|
|
|
/* Fail on real failures, and fallback to simple pipe if pipe2 is unsupported. */
|
|
|
|
if (errno != ENOSYS && errno != EINVAL)
|
|
|
|
return -1;
|
|
|
|
pipe_flags = 0;
|
|
|
|
} else {
|
|
|
|
/* If the flags on both ends are identical, no need to do anything else. */
|
|
|
|
if ((O_CLOEXEC | read_flags) == (O_CLOEXEC | write_flags))
|
|
|
|
return 0;
|
|
|
|
/* Clear the flags which have already been set using pipe2. */
|
|
|
|
read_flags &= ~pipe_flags;
|
|
|
|
write_flags &= ~pipe_flags;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* When we reach here with pipe_flags of 0, it means pipe2 failed (or was not attempted),
|
|
|
|
* so we try to use pipe. Otherwise, we skip and proceed to set specific flags below. */
|
|
|
|
if (pipe_flags == 0 && pipe(fds))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* File descriptor flags.
|
|
|
|
* Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. */
|
|
|
|
if (read_flags & O_CLOEXEC)
|
|
|
|
if (fcntl(fds[0], F_SETFD, FD_CLOEXEC))
|
|
|
|
goto error;
|
|
|
|
if (write_flags & O_CLOEXEC)
|
|
|
|
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* File status flags after clearing the file descriptor flag O_CLOEXEC. */
|
|
|
|
read_flags &= ~O_CLOEXEC;
|
|
|
|
if (read_flags)
|
|
|
|
if (fcntl(fds[0], F_SETFL, read_flags))
|
|
|
|
goto error;
|
|
|
|
write_flags &= ~O_CLOEXEC;
|
|
|
|
if (write_flags)
|
|
|
|
if (fcntl(fds[1], F_SETFL, write_flags))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
close(fds[0]);
|
|
|
|
close(fds[1]);
|
|
|
|
return -1;
|
|
|
|
}
|