static int setsocketnonblock(int sfd)
{
int flags;
flags = fcntl(sfd, F_GETFL, 0);
if (flags < 0) {
return -1;
}
return fcntl(sfd, F_SETFL, flags | O_NONBLOCK);
}
static int opensocketnonblock (const char *host, const char *port, int timeout_ms, char errmsg[128])
{
struct addrinfo hints;
struct addrinfo *res, *rp;
int err, sockfd = -1;
*errmsg = 0;
/* Obtain address(es) matching host/port */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
err = getaddrinfo(host, port, &hints, &res);
if (err != 0) {
snprintf(errmsg, 128, "getaddrinfo error(%d): %s", err, gai_strerror(err));
return -1;
}
/**
* getaddrinfo() returns a list of address structures.
*
* Try each address until we successfully connect(2).
* If socket(2) (or connect(2)) fails, we (close the socket and)
* try the next address.
*/
for (rp = res; rp != NULL; rp = rp->ai_next) {
/**
* socket()
*
* create an endpoint for communication
* https://linux.die.net/man/2/socket
* On success, a file descriptor for the new socket is returned.
* On error, -1 is returned, and errno is set appropriately.
*/
sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sockfd == -1) {
snprintf(errmsg, 128, "socket error(%d): %s", errno, strerror(errno));
continue;
}
/* connect a socket as nonblock mode */
if (setsocketnonblock(sockfd) != 0) {
snprintf(errmsg, 128, "fcntl error(%d): %s", errno, strerror(errno));
close(sockfd);
continue;
}
err = connect(sockfd, rp->ai_addr, rp->ai_addrlen);
if (err == 0) {
/* connect success */
freeaddrinfo(res);
return sockfd;
}
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINPROGRESS) {
struct pollfd pfd;
pfd.fd = sockfd;
pfd.events = POLLOUT;
pfd.revents = 0;
do {
// dead loop?
err = poll(&pfd, 1, timeout_ms);
} while(err == 0);
if (err == -1) {
snprintf(errmsg, 128, "poll errno(%d): %s", errno, strerror(errno));
close(sockfd);
continue;
}
if (pfd.revents & POLLERR) {
snprintf(errmsg, 128, "poll POLLERR");
close(sockfd);
continue;
}
/* success */
freeaddrinfo(res);
return sockfd;
}
snprintf(errmsg, 128, "connect errno(%d): %s", errno, strerror(errno));
close(sockfd);
}
if (! errmsg[0]) {
snprintf(errmsg, 128, "bad address %s:%s", host, port);
}
freeaddrinfo(res);
return -1;
}