[misc] Use fcntl+O_NONBLOCK when binding instead of socket+SOCK_NONBLOCK
socket() with SOCK_NONBLOCK (O_NONBLOCK) seems not to be possible on MacOS, it yields 'Protocol wrong type for socket'. Switch to using fcntl() and O_NONBLOCK instead, hopefully works better cross-platform. Closes #1644
This commit is contained in:
parent
9d092c983b
commit
54c2667aea
|
@ -359,7 +359,7 @@ httpd_server_new(struct event_base *evbase, unsigned short port, httpd_request_c
|
|||
server->request_cb = cb;
|
||||
server->request_cb_arg = arg;
|
||||
|
||||
server->fd = net_bind_with_reuseport(&port, SOCK_STREAM | SOCK_NONBLOCK, "httpd");
|
||||
server->fd = net_bind_with_reuseport(&port, SOCK_STREAM, "httpd");
|
||||
if (server->fd <= 0)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
|
@ -584,6 +585,7 @@ connection_test(int family, const char *address, const char *address_log, int po
|
|||
fd_set fdset;
|
||||
struct timeval timeout = { MDNS_CONNECT_TEST_TIMEOUT, 0 };
|
||||
socklen_t len;
|
||||
int flags;
|
||||
int error;
|
||||
int retval;
|
||||
int ret;
|
||||
|
@ -603,13 +605,29 @@ connection_test(int family, const char *address, const char *address_log, int po
|
|||
return -1;
|
||||
}
|
||||
|
||||
sock = socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, ai->ai_protocol);
|
||||
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (sock < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_MDNS, "Connection test to %s:%d failed with socket error: %s\n", address_log, port, strerror(errno));
|
||||
goto out_free_ai;
|
||||
}
|
||||
|
||||
// For Linux we could just give SOCK_NONBLOCK to socket(), but that won't work
|
||||
// with MacOS, so we have to use fcntl()
|
||||
flags = fcntl(sock, F_GETFL, 0);
|
||||
if (flags < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_MDNS, "Connection test to %s:%d failed with fcntl get flags error: %s\n", address_log, port, strerror(errno));
|
||||
goto out_close_socket;
|
||||
}
|
||||
|
||||
ret = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_MDNS, "Connection test to %s:%d failed with fcntl set flags error: %s\n", address_log, port, strerror(errno));
|
||||
goto out_close_socket;
|
||||
}
|
||||
|
||||
ret = connect(sock, ai->ai_addr, ai->ai_addrlen);
|
||||
if (ret < 0 && errno != EINPROGRESS)
|
||||
{
|
||||
|
|
50
src/misc.c
50
src/misc.c
|
@ -48,6 +48,7 @@
|
|||
# include <pthread_np.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h> // getifaddrs
|
||||
|
@ -222,19 +223,20 @@ net_if_get(char *ifname, size_t ifname_len, const char *addr)
|
|||
return (ifname[0] != 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
int
|
||||
net_connect(const char *addr, unsigned short port, int type, const char *log_service_name)
|
||||
static int
|
||||
net_connect_impl(const char *addr, unsigned short port, int type, const char *log_service_name, bool set_nonblock)
|
||||
{
|
||||
struct addrinfo hints = { 0 };
|
||||
struct addrinfo *servinfo;
|
||||
struct addrinfo *ptr;
|
||||
char strport[8];
|
||||
int flags;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_DBG, L_MISC, "Connecting to '%s' at %s (port %u)\n", log_service_name, addr, port);
|
||||
|
||||
hints.ai_socktype = (type & (SOCK_STREAM | SOCK_DGRAM)); // filter since type can be SOCK_STREAM | SOCK_NONBLOCK
|
||||
hints.ai_socktype = type;
|
||||
hints.ai_family = (cfg_getbool(cfg_getsec(cfg, "general"), "ipv6")) ? AF_UNSPEC : AF_INET;
|
||||
|
||||
snprintf(strport, sizeof(strport), "%hu", port);
|
||||
|
@ -247,14 +249,26 @@ net_connect(const char *addr, unsigned short port, int type, const char *log_ser
|
|||
|
||||
for (ptr = servinfo; ptr; ptr = ptr->ai_next)
|
||||
{
|
||||
fd = socket(ptr->ai_family, type | SOCK_CLOEXEC, ptr->ai_protocol);
|
||||
fd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
||||
if (fd < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// For Linux we could just give SOCK_CLOEXEC to socket(), but that won't
|
||||
// work with MacOS, so we have to use fcntl()
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0)
|
||||
continue;
|
||||
if (set_nonblock)
|
||||
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK | O_CLOEXEC);
|
||||
else
|
||||
ret = fcntl(fd, F_SETFL, flags | O_CLOEXEC);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
ret = connect(fd, ptr->ai_addr, ptr->ai_addrlen);
|
||||
if (ret < 0 && errno != EINPROGRESS) // EINPROGRESS in case of SOCK_NONBLOCK
|
||||
if (ret < 0 && errno != EINPROGRESS) // EINPROGRESS in case of nonblock
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
|
@ -276,8 +290,15 @@ net_connect(const char *addr, unsigned short port, int type, const char *log_ser
|
|||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
net_connect(const char *addr, unsigned short port, int type, const char *log_service_name)
|
||||
{
|
||||
return net_connect_impl(addr, port, type, log_service_name, false);
|
||||
}
|
||||
|
||||
// If *port is 0 then a random port will be assigned, and *port will be updated
|
||||
// with the port number
|
||||
// with the port number. SOCK_STREAM type services are set to use non-blocking
|
||||
// sockets.
|
||||
static int
|
||||
net_bind_impl(short unsigned *port, int type, const char *log_service_name, bool reuseport)
|
||||
{
|
||||
|
@ -289,6 +310,7 @@ net_bind_impl(short unsigned *port, int type, const char *log_service_name, bool
|
|||
const char *cfgaddr;
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
char strport[8];
|
||||
int flags;
|
||||
int yes = 1;
|
||||
int no = 0;
|
||||
int fd = -1;
|
||||
|
@ -296,7 +318,7 @@ net_bind_impl(short unsigned *port, int type, const char *log_service_name, bool
|
|||
|
||||
cfgaddr = cfg_getstr(cfg_getsec(cfg, "general"), "bind_address");
|
||||
|
||||
hints.ai_socktype = (type & (SOCK_STREAM | SOCK_DGRAM)); // filter since type can be SOCK_STREAM | SOCK_NONBLOCK
|
||||
hints.ai_socktype = type;
|
||||
hints.ai_family = (cfg_getbool(cfg_getsec(cfg, "general"), "ipv6")) ? AF_INET6 : AF_INET;
|
||||
hints.ai_flags = cfgaddr ? 0 : AI_PASSIVE;
|
||||
|
||||
|
@ -313,10 +335,22 @@ net_bind_impl(short unsigned *port, int type, const char *log_service_name, bool
|
|||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
fd = socket(ptr->ai_family, type | SOCK_CLOEXEC, ptr->ai_protocol);
|
||||
fd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
// For Linux we could just give SOCK_NONBLOCK and SOCK_CLOEXEC to
|
||||
// socket(), but that won't work with MacOS, so we have to use fcntl()
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0)
|
||||
continue;
|
||||
if (type == SOCK_STREAM)
|
||||
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK | O_CLOEXEC);
|
||||
else
|
||||
ret = fcntl(fd, F_SETFL, flags | O_CLOEXEC);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
// Makes us able to attach multiple threads to the same port
|
||||
if (reuseport)
|
||||
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
|
||||
|
|
|
@ -15,15 +15,6 @@
|
|||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#ifndef SOCK_NONBLOCK
|
||||
#include <fcntl.h>
|
||||
#define SOCK_NONBLOCK O_NONBLOCK
|
||||
#endif
|
||||
|
||||
#ifndef SOCK_CLOEXEC
|
||||
#define SOCK_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
union net_sockaddr
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
|
|
|
@ -4813,7 +4813,7 @@ mpd_init(void)
|
|||
CHECK_NULL(L_MPD, evbase_mpd = event_base_new());
|
||||
CHECK_NULL(L_MPD, cmdbase = commands_base_new(evbase_mpd, NULL));
|
||||
|
||||
mpd_sockfd = net_bind(&port, SOCK_STREAM | SOCK_NONBLOCK, "mpd");
|
||||
mpd_sockfd = net_bind(&port, SOCK_STREAM, "mpd");
|
||||
if (mpd_sockfd < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not bind mpd server to port %hu\n", port);
|
||||
|
|
Loading…
Reference in New Issue