Last active
July 5, 2018 09:11
-
-
Save romuloceccon/c50cc1ebec38fa86bfe42948ea47e8e9 to your computer and use it in GitHub Desktop.
EventMachine/ECONNREFUSED bug on OSX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This program demonstrates a bug with eventmachine on OSX. When /etc/hosts is | |
set like this: | |
127.0.0.1 localhost | |
255.255.255.255 broadcasthost | |
::1 localhost | |
the program will fail with a ECONNREFUSED (61) error when localhost is used | |
to resolve the local interface address. The program shows that in such | |
scenario getaddrinfo returns 4 addrinfo structures, with families 0x1e, 0x1e, | |
0x02, 0x02. When using 127.0.0.1 or 0.0.0.0 only families 0x02, 0x02 are | |
returned. It looks like the correct behavior would be to try connecting to | |
all address until one succeeds (see | |
http://www.tortonesi.com/blog/2015/03/13/fixing-getaddrinfo/). eventmachine | |
tries only the first one, so in the "localhost" case it creates a socket with | |
family set to 0x1e, which appears to be the main reason why ECONNREFUSED is | |
returned. | |
Removing the "::1" line from /etc/hosts workarounds the problem. | |
Compile: $ gcc -o tcptest -Wall tcptest.c | |
Start listener: $ nc -kl 9001 | |
Test: $ tcptest 127.0.0.1 9001; tcptest 0.0.0.0 9001; tcptest localhost 9001 | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <netdb.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <netinet/tcp.h> | |
#include <errno.h> | |
int main(int argc, char **argv) | |
{ | |
struct sockaddr_storage bind_as; | |
size_t bind_as_len; | |
struct addrinfo *ai; | |
struct addrinfo hints; | |
int gai; | |
int sd; | |
int i; | |
struct addrinfo *ptr; | |
if (argc != 3) { | |
printf("invalid args\n"); | |
return 1; | |
} | |
memset(&hints, 0, sizeof(hints)); | |
hints.ai_family = AF_UNSPEC; | |
hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; | |
gai = getaddrinfo(argv[1], argv[2], &hints, &ai); | |
if (gai != 0) { | |
printf("getaddrinfo error: %d\n", gai); | |
return 1; | |
} | |
for (ptr = ai; ptr != NULL; ptr = ptr->ai_next) { | |
printf("%04x: ", ptr->ai_family); | |
for (i = 0; i < ptr->ai_addrlen; i++) | |
printf("%02x ", (unsigned int) *(((unsigned char *) ptr->ai_addr) + i)); | |
printf("\n"); | |
} | |
memcpy(&bind_as, ai->ai_addr, ai->ai_addrlen); | |
bind_as_len = ai->ai_addrlen; | |
freeaddrinfo(ai); | |
sd = socket(bind_as.ss_family, SOCK_STREAM, 0); | |
if (sd == -1) { | |
printf("invalid socket\n"); | |
return 1; | |
} | |
if (connect(sd, (struct sockaddr *)&bind_as, bind_as_len) != 0) { | |
printf("connect error: %d\n", errno); | |
return 1; | |
} | |
printf("ok\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment