ruby-core:29427 SocketError on SnowLeopard
1.9.2-preview3を試そうとしたところ、SocketErrorがでた。調べてたところ、redmineにチケットを見つけた。
どうも http://d.hatena.ne.jp/kimuraw/20100116/p1 と同じことが1.9でも起きてるみたい。
そもそも、
getaddrinfo: nodename nor servname provided, or not known (SocketError)
というエラーは、getaddrinfo(3)のhostnameとservnameの両方が与えられていないときのメッセージだ。TCPServer.new('localhost', 0)なら、譲ってservname=0はなしとみなされたものだとしても、hostnameが与えられていないってことはないよね。
なんかSnow Leopardのgetaddrinfoがあやしい気がしてきたので、そこを調べてみる。
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <stdio.h> int main (int argc, const char * argv[]) { test_addrinfo("localhost", "80"); test_addrinfo("", "80"); test_addrinfo(NULL, "80"); test_addrinfo("localhost", "000"); test_addrinfo("", "000"); test_addrinfo(NULL, "000"); test_addrinfo("localhost", "0"); test_addrinfo("", "0"); test_addrinfo(NULL, "0"); test_addrinfo("localhost", ""); test_addrinfo("", ""); test_addrinfo(NULL, ""); test_addrinfo("localhost", NULL); test_addrinfo("", NULL); test_addrinfo(NULL, NULL); return 0; } int test_addrinfo(const char *host, const char *serv) { struct addrinfo hints; struct addrinfo *res; int error; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_INET; #ifdef AI_NUMERICSERV if (serv && strlen(serv) > 0) { hints.ai_flags |= AI_NUMERICSERV; } #endif error = getaddrinfo(host, serv, &hints, &res); if (error) { printf("[NG]host: %10s, serv: %10s, \n\terror: %s\n", host, serv, gai_strerror(error)); } else { printf("[OK]host: %10s, serv: %10s\n", host, serv); } freeaddrinfo(res); }
これを試してみるとこうなる。
result on 10.6.4
host\serv | "80" | "000" | "0" | "" | NULL |
---|---|---|---|---|---|
"localhost" | OK | NG(!) | NG(!) | OK | OK |
"" | OK | OK | OK | NG | NG |
NULL | OK | OK | OK | NG | NG |
host\serv | "80" | "000" | "0" | "" | NULL |
---|---|---|---|---|---|
"localhost" | OK | OK(!) | OK(!) | OK | OK |
"" | OK | OK | OK | NG | NG |
NULL | OK | OK | OK | NG | NG |
このままでも困るので、とりあえずruby側で対処するパッチをつくってみた。方針は、
- hostnameあり、servnameが数値のゼロと解釈できるときが対象
- servnameをNULLが与えらたものとして処理する
というもの。もし同様の問題で困っている人がいたら、試してみて結果を教えてもらえるとうれしいです。