Forum: Ruby-dev [ruby-trunk - Bug #7100][Open] WEBrick::HTTPServer.new で BindAddress を指定しない場合に必ず警告が記録される

Posted by sho-h (Sho Hashimoto) (Guest)
on 2012-10-02 10:22
(Received via mailing list)
Issue #7100 has been reported by sho-h (Sho Hashimoto).

----------------------------------------
Bug #7100: WEBrick::HTTPServer.new で BindAddress を指定しない場合に必ず警告が記録される
https://bugs.ruby-lang.org/issues/7100

Author: sho-h (Sho Hashimoto)
Status: Open
Priority: Low
Assignee:
Category:
Target version:
ruby -v: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]


=begin
以下のようにすると必ず警告が記録されるようです。

  $ ruby -v -r webrick -e 'WEBrick::HTTPServer.new(Port: 3000)'
  ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [2012-09-04 19:20:48] INFO  WEBrick 1.3.1
  [2012-09-04 19:20:48] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
  [2012-09-04 19:20:48] WARN  TCPServer Error: Address already in use - 
bind(2)

1.8 では記録されませんでした。1.9.1 以降は記録されました。

lib/webrick/utils.rb の WEBrick::Utils#create_listeners が以下のようになっており、

  res = Socket::getaddrinfo(address, port,
                            Socket::AF_UNSPEC,   # address family
                            Socket::SOCK_STREAM, # socket type
                            0,                   # protocol
                            Socket::AI_PASSIVE)  # flag
  last_error = nil
  sockets = []
  res.each{|ai|
    begin
      logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
      sock = TCPServer.new(ai[3], port)
      ...

Socket.getaddrinfo が 1.9 から複数値を返すからのようです。1.8.7 だと 0.0.0.0 の方だけでした。

  $ ruby -v -r pp -r socket -e 'pp Socket::getaddrinfo(nil, 3000, 
Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)'
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [["AF_INET", 3000, "0.0.0.0", "0.0.0.0", 2, 1, 6],
   ["AF_INET6", 3000, "::", "::", 10, 1, 6]]

WEBrick::Utils#create_listeners 
のコメントとマッチしなくなるデメリットがあるのですが、config[:BindAddress] のデフォルト値を 0.0.0.0 か :: 
のどちらかにしてしまうのはいかがでしょう。
=end
Posted by ChultOch5 (Sho Morita) (Guest)
on 2012-10-12 17:08
(Received via mailing list)
Issue #7100 has been updated by ChultOch5 (Sho Morita).


=begin
私のところでも同様の症状が出ています。そういった警告が出る場合、WEBrick サーバーに IPv6 
でアクセスできなくなってしまいます。また、環境によっては http://localhost:3000/ のように localhost 
を指定してもアクセス不能になってしまいます。(:BindAddress を明示的に指定すれば問題ないのですけれども…)

WEBrick のドキュメントには :BindAddress に関して

  デフォルトの nil や "0.0.0.0", "::" などを指定した場合は使用可能なすべてのネットワークインターフェースに対して 
listen を開始します。

と書いてあるものの、実際には

* nil を指定すると使用可能なすべての IPv4 および IPv6 ネットワークインターフェースに対して listen する。
* "0.0.0.0" を指定すると使用可能なすべての IPv4 ネットワークインターフェースに対してのみ listen する。
* "::" を指定すると使用可能なすべての IPv6 ネットワークインターフェースに対してのみ listen する。(一部システムでは nil 
と指定したのと同様に、すべての IPv4 と IPv6 ネットワークインターフェースに対して listen する)

となります。

一部のシステム(Linuxなど)では、IPv6 ワイルドカードアドレスである :: を bind すると、IPv6 
ネットワークインターフェースだけではなく、IPv4 ネットワークインターフェースも bind されます。0.0.0.0 と :: を両方 
bind しようとすると、後から bind した方が Address already in use(EADDRINUSE) 
エラーとなり失敗するため、IPv6 でのアクセスが行えなくなってしまいます。

* http://www.a-k-r.org/pub/socket-rubykaigi2009.pdf
* http://codezine.jp/article/detail/5395
* NEWS-1.9.2

などによると、Ruby 1.9.2 で Socket にたくさん機能が追加され、IPv6 問題がだいぶ改善されたようです。そこで追加された 
Socket#ipv6only! を呼び出すと IPV6_V6ONLY ソケットオプションが有効になり、0.0.0.0 と :: の両方を 
bind する事ができるようになります。

しかし、現在 WEBrick が使用している TCPServer は従来からある API で、IPV6_V6ONLY 
ソケットオプションをセットしません。新しい API である Socket.tcp_server_sockets などは、必要に応じて 
Socket#ipv6only! を呼び出してくれます。

ですので、TCPServer.new ではなく Socket.tcp_server_sockets 
を使うようにするのが良いかと思ったのですが、TCPServer.new のオブジェクトと Socket.tcp_server_sockets 
のオブジェクトは一部互換性の無い部分があり、置き換えてしまうと WEBrick 
を利用する既存のソフトウェアで互換性問題が起こるのではないか心配です。

そこで、互換性の問題が起こらないように TCPServer を使うのを維持しつつ問題を修正しようとすると、

* :BindAddress == nil である場合に、:: を先に bind し、0.0.0.0 を後から bind (その際 
EADDRINUSE が起きても無視)する。

というようにすれば良いのではないかと思います。一応パッチを作ってみました。

  Index: lib/webrick/utils.rb
  ===================================================================
  --- lib/webrick/utils.rb  (revision 37168)
  +++ lib/webrick/utils.rb  (working copy)
  @@ -79,6 +79,15 @@
                                   Socket::AI_PASSIVE)  # flag
         last_error = nil
         sockets = []
  +      # If address == nil, Socket.getaddrinfo returns 2 entries, the
  +      # IPv4 wildcard address "0.0.0.0" and the IPv6 wildcard address 
"::".
  +      # On some systems, if you try to bind for both "0.0.0.0" and 
"::",
  +      # the later one will fails.
  +      # To workaround such behaviour, try bind for "::" first, then
  +      # bind "0.0.0.0" and ignore EADDRINUSE error.
  +      if address.nil?
  +        res = res.sort_by{|i| i[4]}.reverse
  +      end
         res.each{|ai|
           begin
             logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
  @@ -87,7 +96,9 @@
             Utils::set_close_on_exec(sock)
             sockets << sock
           rescue => ex
  -          logger.warn("TCPServer Error: #{ex}") if logger
  +          logger.warn("TCPServer Error: #{ex}") if logger and 
!(address.nil? and
  +                                                                ai[4] 
== Socket::AF_INET and
  + 
Errno::EADDRINUSE === ex)
             last_error  = ex
           end
         }


ちなみに、根本的な解決にはなりませんが、

  # sysctl net.ipv6.bindv6only=1

とすると、IPV6_V6ONLY ソケットオプションがデフォルトで有効になり、:BindAddress が nil でも警告は出なくなります。

=end
----------------------------------------
Bug #7100: WEBrick::HTTPServer.new で BindAddress を指定しない場合に必ず警告が記録される
https://bugs.ruby-lang.org/issues/7100#change-30447

Author: sho-h (Sho Hashimoto)
Status: Open
Priority: Low
Assignee:
Category:
Target version:
ruby -v: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]


=begin
以下のようにすると必ず警告が記録されるようです。

  $ ruby -v -r webrick -e 'WEBrick::HTTPServer.new(Port: 3000)'
  ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [2012-09-04 19:20:48] INFO  WEBrick 1.3.1
  [2012-09-04 19:20:48] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
  [2012-09-04 19:20:48] WARN  TCPServer Error: Address already in use - 
bind(2)

1.8 では記録されませんでした。1.9.1 以降は記録されました。

lib/webrick/utils.rb の WEBrick::Utils#create_listeners が以下のようになっており、

  res = Socket::getaddrinfo(address, port,
                            Socket::AF_UNSPEC,   # address family
                            Socket::SOCK_STREAM, # socket type
                            0,                   # protocol
                            Socket::AI_PASSIVE)  # flag
  last_error = nil
  sockets = []
  res.each{|ai|
    begin
      logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
      sock = TCPServer.new(ai[3], port)
      ...

Socket.getaddrinfo が 1.9 から複数値を返すからのようです。1.8.7 だと 0.0.0.0 の方だけでした。

  $ ruby -v -r pp -r socket -e 'pp Socket::getaddrinfo(nil, 3000, 
Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)'
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [["AF_INET", 3000, "0.0.0.0", "0.0.0.0", 2, 1, 6],
   ["AF_INET6", 3000, "::", "::", 10, 1, 6]]

WEBrick::Utils#create_listeners 
のコメントとマッチしなくなるデメリットがあるのですが、config[:BindAddress] のデフォルト値を 0.0.0.0 か :: 
のどちらかにしてしまうのはいかがでしょう。
=end
Posted by mame (Yusuke Endoh) (Guest)
on 2012-11-05 13:37
(Received via mailing list)
Issue #7100 has been updated by mame (Yusuke Endoh).

Status changed from Open to Assigned
Assignee set to akr (Akira Tanaka)
Target version set to 2.0.0

akr さん、どう思われますか?

--
Yusuke Endoh <mame@tsg.ne.jp>
----------------------------------------
Bug #7100: WEBrick::HTTPServer.new で BindAddress を指定しない場合に必ず警告が記録される
https://bugs.ruby-lang.org/issues/7100#change-32403

Author: sho-h (Sho Hashimoto)
Status: Assigned
Priority: Low
Assignee: akr (Akira Tanaka)
Category:
Target version: 2.0.0
ruby -v: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]


=begin
以下のようにすると必ず警告が記録されるようです。

  $ ruby -v -r webrick -e 'WEBrick::HTTPServer.new(Port: 3000)'
  ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [2012-09-04 19:20:48] INFO  WEBrick 1.3.1
  [2012-09-04 19:20:48] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
  [2012-09-04 19:20:48] WARN  TCPServer Error: Address already in use - 
bind(2)

1.8 では記録されませんでした。1.9.1 以降は記録されました。

lib/webrick/utils.rb の WEBrick::Utils#create_listeners が以下のようになっており、

  res = Socket::getaddrinfo(address, port,
                            Socket::AF_UNSPEC,   # address family
                            Socket::SOCK_STREAM, # socket type
                            0,                   # protocol
                            Socket::AI_PASSIVE)  # flag
  last_error = nil
  sockets = []
  res.each{|ai|
    begin
      logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
      sock = TCPServer.new(ai[3], port)
      ...

Socket.getaddrinfo が 1.9 から複数値を返すからのようです。1.8.7 だと 0.0.0.0 の方だけでした。

  $ ruby -v -r pp -r socket -e 'pp Socket::getaddrinfo(nil, 3000, 
Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)'
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [["AF_INET", 3000, "0.0.0.0", "0.0.0.0", 2, 1, 6],
   ["AF_INET6", 3000, "::", "::", 10, 1, 6]]

WEBrick::Utils#create_listeners 
のコメントとマッチしなくなるデメリットがあるのですが、config[:BindAddress] のデフォルト値を 0.0.0.0 か :: 
のどちらかにしてしまうのはいかがでしょう。
=end
Posted by Tanaka Akira (Guest)
on 2012-11-06 10:34
(Received via mailing list)
2012/11/5 mame (Yusuke Endoh) <mame@tsg.ne.jp>:
>
> akr $B$5$s!"$I$&;W$o$l$^$9$+!)(B

IPv4-mapped IPv6 address 
$B$O%W%i%C%H%U%)!<%`$K$h$C$FMxMQ$G$-$?$j$G$-$J$+$C$?$j$9$k$N$G!"(B
$B$I$N%W%i%C%H%U%)!<%`$G$bMxMQ$7$J$$$GF0:n$9$k$h$&$K$9$k$N$,F0:n$,0l4S$7$F$h$$$H;W$$$^$9!#(B

$B;d$O!"D94|E*$K$O!"(BTCPServer $B$H$+$r;H$&$N$O$d$a$F!"(B
Socket $B$K0\9T$7$FM_$7$$$H;W$C$F$$$^$9$,!"(B
$B8_49@-$NLdBj$rL5;k$9$k$3$H$b$G$-$^$;$s$h$M!#(B

$B$H$$$&$U$?$D$NE@$r9M$($k$H!"(BSocket.tcp_server_sockets 
$B$G%=%1%C%H$r:n$C$F!"(B
$B$=$3$+$i(B TCPServer.for_fd $B$G(B TCPServer 
$B$N%$%s%9%?%s%9$r:n$k$"$?$j$+$J$!!"$H(B
$B;W$$$^$9!#(B
Posted by akr (Akira Tanaka) (Guest)
on 2012-11-07 11:21
(Received via mailing list)
Issue #7100 has been updated by akr (Akira Tanaka).

File webrick-dont-use-ipv4-mapped-ipv6-address.patch added

webrick-dont-use-ipv4-mapped-ipv6-address.patch みたいなかんじかなぁ。
----------------------------------------
Bug #7100: WEBrick::HTTPServer.new で BindAddress を指定しない場合に必ず警告が記録される
https://bugs.ruby-lang.org/issues/7100#change-32549

Author: sho-h (Sho Hashimoto)
Status: Assigned
Priority: Low
Assignee: akr (Akira Tanaka)
Category:
Target version: 2.0.0
ruby -v: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]


=begin
以下のようにすると必ず警告が記録されるようです。

  $ ruby -v -r webrick -e 'WEBrick::HTTPServer.new(Port: 3000)'
  ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [2012-09-04 19:20:48] INFO  WEBrick 1.3.1
  [2012-09-04 19:20:48] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
  [2012-09-04 19:20:48] WARN  TCPServer Error: Address already in use - 
bind(2)

1.8 では記録されませんでした。1.9.1 以降は記録されました。

lib/webrick/utils.rb の WEBrick::Utils#create_listeners が以下のようになっており、

  res = Socket::getaddrinfo(address, port,
                            Socket::AF_UNSPEC,   # address family
                            Socket::SOCK_STREAM, # socket type
                            0,                   # protocol
                            Socket::AI_PASSIVE)  # flag
  last_error = nil
  sockets = []
  res.each{|ai|
    begin
      logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
      sock = TCPServer.new(ai[3], port)
      ...

Socket.getaddrinfo が 1.9 から複数値を返すからのようです。1.8.7 だと 0.0.0.0 の方だけでした。

  $ ruby -v -r pp -r socket -e 'pp Socket::getaddrinfo(nil, 3000, 
Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)'
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [["AF_INET", 3000, "0.0.0.0", "0.0.0.0", 2, 1, 6],
   ["AF_INET6", 3000, "::", "::", 10, 1, 6]]

WEBrick::Utils#create_listeners 
のコメントとマッチしなくなるデメリットがあるのですが、config[:BindAddress] のデフォルト値を 0.0.0.0 か :: 
のどちらかにしてしまうのはいかがでしょう。
=end
Posted by mame (Yusuke Endoh) (Guest)
on 2013-02-18 15:55
(Received via mailing list)
Issue #7100 has been updated by mame (Yusuke Endoh).

Target version changed from 2.0.0 to next minor

ちょっと今からだと怖すぎるのと、BindAddress を指定するという workaround があるようなので、
すみませんが一旦 next minor に。
2.0.0-pXXX で直すかどうかは nagachika さんにお任せします。

--
Yusuke Endoh <mame@tsg.ne.jp>
----------------------------------------
Bug #7100: WEBrick::HTTPServer.new で BindAddress を指定しない場合に必ず警告が記録される
https://bugs.ruby-lang.org/issues/7100#change-36535

Author: sho-h (Sho Hashimoto)
Status: Assigned
Priority: Low
Assignee: akr (Akira Tanaka)
Category:
Target version: next minor
ruby -v: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]


=begin
以下のようにすると必ず警告が記録されるようです。

  $ ruby -v -r webrick -e 'WEBrick::HTTPServer.new(Port: 3000)'
  ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [2012-09-04 19:20:48] INFO  WEBrick 1.3.1
  [2012-09-04 19:20:48] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
  [2012-09-04 19:20:48] WARN  TCPServer Error: Address already in use - 
bind(2)

1.8 では記録されませんでした。1.9.1 以降は記録されました。

lib/webrick/utils.rb の WEBrick::Utils#create_listeners が以下のようになっており、

  res = Socket::getaddrinfo(address, port,
                            Socket::AF_UNSPEC,   # address family
                            Socket::SOCK_STREAM, # socket type
                            0,                   # protocol
                            Socket::AI_PASSIVE)  # flag
  last_error = nil
  sockets = []
  res.each{|ai|
    begin
      logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
      sock = TCPServer.new(ai[3], port)
      ...

Socket.getaddrinfo が 1.9 から複数値を返すからのようです。1.8.7 だと 0.0.0.0 の方だけでした。

  $ ruby -v -r pp -r socket -e 'pp Socket::getaddrinfo(nil, 3000, 
Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)'
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
  [["AF_INET", 3000, "0.0.0.0", "0.0.0.0", 2, 1, 6],
   ["AF_INET6", 3000, "::", "::", 10, 1, 6]]

WEBrick::Utils#create_listeners 
のコメントとマッチしなくなるデメリットがあるのですが、config[:BindAddress] のデフォルト値を 0.0.0.0 か :: 
のどちらかにしてしまうのはいかがでしょう。
=end
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.