socket: use accept4 when available

Linux, *BSD, and Solaris support accept4 system call that enables the
caller to assign additional flags and save some extra system calls. It
can come in handy when O_NONBLOCK or/and FD_CLOEXEC is/are required on a
socket after being accepted.

Ref:
https://man7.org/linux/man-pages/man2/accept.2.html
https://man.freebsd.org/cgi/man.cgi?query=accept4
https://man.dragonflybsd.org/?command=accept&section=2
https://man.openbsd.org/accept.2
https://man.netbsd.org/accept.2
https://docs.oracle.com/cd/E88353_01/html/E37843/accept4-3c.html
https://www.gnu.org/software/gnulib/manual/html_node/accept4.html

Closes #16979
This commit is contained in:
Andy Pan 2025-04-05 23:55:32 +08:00 committed by Daniel Stenberg
parent 2f5e4e0db4
commit 3d02872be7
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
8 changed files with 52 additions and 2 deletions

View file

@ -26,6 +26,15 @@ if(NOT UNIX)
message(FATAL_ERROR "This file should be included on Unix platforms only")
endif()
if(APPLE OR
CYGWIN)
set(HAVE_ACCEPT4 0)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
set(HAVE_ACCEPT4 1)
endif()
set(HAVE_ALARM 1)
if(ANDROID)
set(HAVE_ARC4RANDOM 1)

View file

@ -85,6 +85,7 @@ else()
set(HAVE_ATOMIC 0)
endif()
set(HAVE_ACCEPT4 0)
set(HAVE_ALARM 0)
set(HAVE_ARC4RANDOM 0)
set(HAVE_ARPA_INET_H 0)

View file

@ -282,7 +282,7 @@ endif()
include(PickyWarnings)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
string(APPEND CMAKE_C_FLAGS " -D_GNU_SOURCE") # Required for sendmmsg()
string(APPEND CMAKE_C_FLAGS " -D_GNU_SOURCE") # Required for sendmmsg() and accept4()
endif()
option(ENABLE_DEBUG "Enable curl debug features (for developing curl itself)" OFF)
@ -1732,6 +1732,7 @@ elseif(DOS)
list(APPEND CMAKE_REQUIRED_LIBRARIES "${WATT_ROOT}/lib/libwatt.a")
endif()
check_function_exists("accept4" HAVE_ACCEPT4)
check_function_exists("fnmatch" HAVE_FNMATCH)
check_symbol_exists("basename" "${CURL_INCLUDES};string.h" HAVE_BASENAME) # libgen.h unistd.h
check_symbol_exists("opendir" "dirent.h" HAVE_OPENDIR)

View file

@ -574,7 +574,7 @@ esac
AM_CONDITIONAL(BUILD_UNITTESTS, test x$supports_unittests = xyes)
# In order to detect support of sendmmsg(), we need to escape the POSIX
# In order to detect support of sendmmsg() and accept4(), we need to escape the POSIX
# jail by defining _GNU_SOURCE or <sys/socket.h> will not expose it.
case $host_os in
linux*)
@ -4112,6 +4112,7 @@ case $host in
esac
AC_CHECK_FUNCS([\
accept4 \
eventfd \
fnmatch \
geteuid \

View file

@ -2144,7 +2144,12 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
if(0 == getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
size = sizeof(add);
#ifdef HAVE_ACCEPT4
s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size,
SOCK_NONBLOCK | SOCK_CLOEXEC);
#else
s_accepted = accept(ctx->sock, (struct sockaddr *) &add, &size);
#endif
}
if(CURL_SOCKET_BAD == s_accepted) {
@ -2153,7 +2158,9 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
}
infof(data, "Connection accepted from server");
#ifndef HAVE_ACCEPT4
(void)curlx_nonblock(s_accepted, TRUE); /* enable non-blocking */
#endif
/* Replace any filter on SECONDARY with one listening on this socket */
ctx->listening = FALSE;
ctx->accepted = TRUE;

View file

@ -197,6 +197,9 @@
/* Define to 1 if you have _Atomic support. */
#cmakedefine HAVE_ATOMIC 1
/* Define to 1 if you have the `accept4' function. */
#cmakedefine HAVE_ACCEPT4 1
/* Define to 1 if you have the `fnmatch' function. */
#cmakedefine HAVE_FNMATCH 1

View file

@ -378,6 +378,24 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
return sockfd;
}
#ifdef HAVE_ACCEPT4
curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen,
int flags,
int line, const char *source)
{
struct sockaddr *addr = (struct sockaddr *)saddr;
curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
curl_socket_t sockfd = accept4(s, addr, addrlen, flags);
if(source && (sockfd != CURL_SOCKET_BAD))
curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
source, line, sockfd);
return sockfd;
}
#endif
/* separate function to allow libcurl to mark a "faked" close */
void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
{

View file

@ -92,6 +92,11 @@ CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
int line, const char *source);
CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
int line, const char *source);
#ifdef HAVE_ACCEPT4
CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr,
void *saddrlen, int flags,
int line, const char *source);
#endif
#ifdef HAVE_SOCKETPAIR
CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol,
curl_socket_t socket_vector[2],
@ -159,6 +164,11 @@ CURL_EXTERN ALLOC_FUNC
#undef accept /* for those with accept as a macro */
#define accept(sock,addr,len)\
curl_dbg_accept(sock, addr, len, __LINE__, __FILE__)
#ifdef HAVE_ACCEPT4
#undef accept4 /* for those with accept4 as a macro */
#define accept4(sock,addr,len,flags)\
curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__)
#endif
#ifdef HAVE_SOCKETPAIR
#define socketpair(domain,type,protocol,socket_vector)\
curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \