diff --git a/src/httpd_libevhttp.c b/src/httpd_libevhttp.c index 6949df8a..b95b5737 100644 --- a/src/httpd_libevhttp.c +++ b/src/httpd_libevhttp.c @@ -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; diff --git a/src/mdns_avahi.c b/src/mdns_avahi.c index dcda47f2..d0102071 100644 --- a/src/mdns_avahi.c +++ b/src/mdns_avahi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -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) { diff --git a/src/misc.c b/src/misc.c index 0f7c7d78..c7c383ce 100644 --- a/src/misc.c +++ b/src/misc.c @@ -48,6 +48,7 @@ # include #endif +#include #include #include #include // 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)); diff --git a/src/misc.h b/src/misc.h index d2716e73..d77393f4 100644 --- a/src/misc.h +++ b/src/misc.h @@ -15,15 +15,6 @@ #include #include -#ifndef SOCK_NONBLOCK -#include -#define SOCK_NONBLOCK O_NONBLOCK -#endif - -#ifndef SOCK_CLOEXEC -#define SOCK_CLOEXEC 0 -#endif - union net_sockaddr { struct sockaddr_in sin; diff --git a/src/mpd.c b/src/mpd.c index 49b83af3..8c459269 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -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);