開発時など、突発的に適当なポートの開閉を調べたい時に、 Netcat を使って
nc -v -z host port
という風にチェックすることがあると思う。
(Nmap や telnet を使うケースもあると思う)
同僚が OS X 上でこの方法でポートの開閉を調べようとしていたのだが、なぜだか結果が 2 つ出力されていた。
最初は謎だと思っていたのだが、原因が分かったので書いておく。
$ nc -v -z localhost 8000
nc: connectx to localhost port 8000 (tcp) failed: Connection refused
nc: connectx to localhost port 8000 (tcp) failed: Connection refused
最初は OS X 固有の現象かと思ったが
実験したところ同じ設定をすれば OS X 以外でも普通に起きることだとわかった。
なお OS X 上では Homebrew などを使って GNU Netcat を優先している方も居ると思う。
その場合は再現しない。再現したければ /usr/bin/nc
と明示すれば良い。
/etc/hosts を見よ
初期状態の OS X の /etc/hosts はこのようになっていた。
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
見ての通り、 localhost が IPv4 と IPv6 でそれぞれエントリが書かれている。
そのため 2 箇所ともチェックしているようだ。
解決方法 1: 明示的にどちらかを指定する
localhost
と書かずに、 IP そのままで書けば問題ない。
$ nc -v -z 127.0.0.1 8000
nc: connectx to 127.0.0.1 port 8000 (tcp) failed: Connection refused
$ nc -v -z ::1 8000
nc: connectx to ::1 port 8000 (tcp) failed: Connection refused
解決方法 2: オプションで IPv4, IPv6 のどちらかを明示すればよい
$ man nc
して読み進めていくと分かるのだが、 -4
や -6
のオプションでそれぞれ IPv4, IPv6 を強制できる
$ nc -v -z -4 localhost 8000
nc: connectx to localhost port 8000 (tcp) failed: Connection refused
$ nc -v -z -6 localhost 8000
nc: connectx to localhost port 8000 (tcp) failed: Connection refused
OpenBSD の nc や FreeBSD 5.4 以上の nc も同様だ。
どうやら BSD 由来の netcat は全てこのようであるらしい。
GNU Netcat にはこのようなオプションはない。
Nmap の付属コマンドとして広く使われている Ncat にもこのオプションがある。
BSD と GNU と書いてあると混乱しそうだが、最近の有名な Linux ディストリビューションも、
BSD 由来の Netcat が入っていることが多い。
(例えば Ubuntu 14 の Netcat をチェックしたが、 BSD 由来の Netcat だった。)
また、 BusyBox の nc にもこのオプションはない。
解決方法 3: /etc/hosts から片方を削除する
なるほど、たしかに 1 つになる。
しかし、このような破壊的な変更は個人的にはおすすめしない。
解決方法 4: GNU Netcat を使う
上で言及したように GNU Netcat にはそのようなオプションはない。
実は Homebrew を使ってインストールする際に brew install netcat
でインストールされるのも GNU Netcat である。
Source: netcat.rb
$ /usr/local/bin/nc -v -z localhost 8000
localhost [127.0.0.1] 8000 (irdmi): Connection refused
この Netcat は安定しているが、最新版のリリース日が 2004 年である。
保守状況がやや気になるので、個人的にはこの解決方法はおすすめしない。
オマケ
Q. もっとたくさん /etc/hosts に書いたらどうなるか?
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
127.0.0.2 localhost
127.0.0.3 localhost
127.0.0.4 localhost
255.255.255.255 broadcasthost
::1 localhost
A. それぞれに対して試行する
$ /usr/bin/nc -v -z -G 1 localhost 8000
nc: connectx to localhost port 8000 (tcp) failed: Connection refused
nc: connectx to localhost port 8000 (tcp) failed: Connection refused
nc: connectx to localhost port 8000 (tcp) failed: Operation timed out
nc: connectx to localhost port 8000 (tcp) failed: Operation timed out
nc: connectx to localhost port 8000 (tcp) failed: Operation timed out
つまり -4
, -6
オプションの話も、あくまで IPv4 と IPv6 と 1 つずつしか書かれていない場合にだけ有効な話だということ。
(なお上の例では、待つのが面倒なので -G
オプションでタイムアウトを 1 秒にしている。)
Q. 片方が Open, もう片方が Close だった場合、終了ステータスはどうなる?
$ nc -v -z localhost 8000 ; echo $?
nc: connectx to localhost port 8000 (tcp) failed: Connection refused
found 0 associations
found 1 connections:
1: flags=XX<CONNECTED,PREFERRED>
outif XXX
src 127.0.0.1 port XXXXX
dst 127.0.0.1 port 8000
rank info not available
TCP aux info available
Connection to localhost port 8000 [tcp/irdmi] succeeded!
0
実験してみたところ 0
を返した。どうやら接続成功と解釈するようだ (?)
片方だけ失敗して片方だけ成功すると、上の例のように失敗した方について詳細が表示される。