きりかノート 3冊め

おあそびプログラミング

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
result on 10.5.8

host\serv "80""000""0" ""NULL
"localhost"OK OK(!)OK(!)OK OK
"" OK OK OK NG NG
NULL OK OK OK NG NG
Snow LeopardLeopardで結果がちがうところ(!)に注目。これはLeopardの動作のほうが正しいよねえ。

このままでも困るので、とりあえずruby側で対処するパッチをつくってみた。方針は、

  • hostnameあり、servnameが数値のゼロと解釈できるときが対象
  • servnameをNULLが与えらたものとして処理する

というもの。もし同様の問題で困っている人がいたら、試してみて結果を教えてもらえるとうれしいです。