I'm trying to port a software that is totally async, and use c-ares, a DNS resolver async library (ii's also used by libcurl or node.js internally).
I've found a problem with the XNU/libc version I'm working on (Darwin Kernel Version 13.0.0: Thu Sep 19 22:22:27 PDT 2013; root:xnu-2422.1.72~6/RELEASE_X86_64)
To make it simple, when I'm using a "connected" UDP socket to make the DNS request, and if a ICMP error happens because there is no DNS server actually, then it's not possible to know the ICMP error happens with poll (and kqueue I think...)
I wonder if there is a correct way, or a hack, to detect this.
I wrote a simple sample program, that works fine on Linux (poll exit giving POLLERR) when trying to make a UDP request to a bad server.
When running on my Mac 10.9, poll exits with 0, which would mean poll timeout, with I have no timeout...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
int flags;
int ret;
char buffer[256];
struct pollfd polldata;
unsigned char req[] = { 0xd3, 0xec, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 };
unsigned char bigbuf[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
/* Set the socket non-blocking. */
flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
/* Connect to the server. */
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0 && errno != EINPROGRESS && errno != ENETUNREACH)
error("ERROR connecting");
// POLLOUT for connect (means nothing for udp socket...)
polldata.fd = sockfd;
polldata.events = POLLOUT;
ret = poll(&polldata, 1, -1);
if (ret == 1 && (polldata.revents & POLLHUP)) {
printf("poll ret %d revents %x disconnected\n", ret, polldata.revents);
exit(0);
}
if (ret == 1 && (polldata.revents & POLLOUT)) {
printf("poll ret %d revents %x POLLOUT connected\n", ret, polldata.revents);
}
printf("poll ret %d revents %x\n", ret, polldata.revents);
ret = send(sockfd, req, sizeof(req), 0);
printf("send ret %d\n", ret);
// POLLIN for data
polldata.fd = sockfd;
polldata.events = (-1) & ~POLLOUT & POLLNVAL;
polldata.events = POLLIN |POLLPRI|POLLOUT|POLLRDNORM|POLLWRNORM|POLLRDBAND|POLLWRBAND;
polldata.events = POLLIN |POLLPRI|POLLRDNORM|POLLRDBAND;
errno=0;
do {
ret = poll(&polldata, 1, -1);
if (ret == 1 && (polldata.revents & POLLHUP)) {
printf("poll ret %d revents %x disconnected\n", ret, polldata.revents);
exit(0);
}
if (ret == 0) {
printf("poll ret 0 while no timeout, should not happen\n");
break;
} else {
printf("poll ret %d revents %x errno %d\n", ret, polldata.revents, errno);
}
} while (ret == 0);
ret = recv(sockfd, bigbuf, sizeof(bigbuf), 0);
printf("recv ret %d errno %d\n", ret, errno); // ECONNREFUSED = 61
close(sockfd);
return 0;
}