はじめに
nc コマンドを使いこなせれば、ネットワークに関するおおよその調査ができると教わったので、使い方をメモしてみます。
そもそものきっかけは、dockerの公式Imageから作ったUbuntuコンテナが、デフォルトだとwget
もtelnet
もインストールできなかったからです。
nc はデフォルトで入っていたので、これを機にまとめることにしました。
おおよそ Wikipedia と man を参考にして書いています。
使用例は http://www.computerhope.com/unix/nc.htm も参考になりそうです。
簡易まとめ表
コマンド | 用途 |
---|---|
echo -en "GET / HTTP/1.1\n\n" | nc localhost 80 | HTTP GET リクエスト |
nc -zv localhost 1-65535 | ポートスキャニング |
(echo 'set KEY1 hoge'; sleep 1s; echo 'key *') | nc localhost 6379 | Redisサーバーにアクセス |
while : ; do (echo -ne "HTTP/1.0 200 Ok\nContent-Length: $(wc -c < response.txt)\n\n"; cat response.txt) | nc -l -p 8080; done | Mockサーバー |
nc -l 8080 0<backpipe | nc localhost 80 1>backpip | Proxy |
/bin/nc.openbsd -x proxy_host:1080 %h %p | Socksサーバー経由でSSH接続用(BSD版に限る) |
Webサーバーに問い合わせ
GETリクエストを投げる
root@abd32f2b7776:/# echo -en "GET / HTTP/1.1\n\n" | nc www.google.com 80
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: http://www.google.co.jp/?gfe_rd=cr&ei=pSYgVIaBEMyT8QfJooHADw
Content-Length: 261
Date: Mon, 22 Sep 2014 13:39:49 GMT
Server: GFE/2.0
Alternate-Protocol: 80:quic,p=0.002
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.jp/?gfe_rd=cr&ei=pSYgVIaBEMyT8QfJooHADw">here</A>.
</BODY></HTML>
レスポンスコードのみ出力
root@abd32f2b7776:/# echo -en "GET / HTTP/1.1\n\n" | nc www.google.com 80 | head -n 1
HTTP/1.1 302 Found
head
コマンドを使っているだけです。
存在しないサーバーを指定した場合
root@abd32f2b7776:/# echo -en "GET / HTTP/1.1\n\n" | nc noexist 80
nc: getaddrinfo: Name or service not known
Listenしてないポートを指定した場合
root@9bd49622dea9:/# echo -en "GET / HTTP/1.1\n\n" | nc web1 8080
root@9bd49622dea9:/# echo $?
1
なにも出力されないので、終了コードを見るしかないようです。
Port Scanning
root@abd32f2b7776:/# nc -vz web1 1-65535 2>&1 | grep succeeded
Connection to web1 22 port [tcp/ssh] succeeded!
Connection to web1 80 port [tcp/http] succeeded!
※追記
ポートスキャニングは、場合によっては攻撃とみなされる可能性があるので、
外部サーバーやAWSなどのIaaSでの使用は気を付けた方が良いそうです。
Redis サーバーにアクセス
root@9bd49622dea9:/# nc redis 6379
set key Hello
+OK
get key
$5
Hello
del key
:1
keys *
*0
quit
+OK
telnet
と同じようことができます。
ワンライナー版
root@9bd49622dea9:/# (echo 'set key Hello'; sleep 1s; echo 'get key'; sleep 1s; echo 'del key'; sleep 1s; echo 'keys *'; sleep 1s) | nc redis 6379
+OK
$5
Hello
:1
*0
こちらも telnet
と同じ感じでした。
sleep を挟まないと、レスポンスを得る前に次のコマンドを実行してしまうところも一緒です。
ただし、telnetと違ってリモートアクセスするコマンドではないので、quitはいりません。
=> Ctrl-C で中断できる
Mock サーバーっぽいもの
サーバー側
nc
コマンドで、適当なポートをListen状態にしておきます。
root@446c957cfaef:~# while : ; do (echo -ne "HTTP/1.0 200 Ok\nContent-Length: $(wc -c < response.txt)\n\n"; cat response.txt) | nc -l -p 8080; done
一度アクセスを受け付けるとプロセスが終了するので、無限ループで起動し続けています。
-k
オプションで何度もアクセスを受け付けられる仕組みがあるのですが、echo部分が一度しかncに渡されないので、2回目以降はレスポンスが得られません。
クライアント側
root@9bd49622dea9:/# nc web1 8080
HTTP/1.0 200 Ok
Content-Length: 14
Response File
response.txt を相応しいものにしておけば、簡単なMockサーバーを用意できそうですね。
もちろん、GETリクエストでも同じレスポンスが得られます。
root@9bd49622dea9:/# echo -en "GET / HTTP/1.1\n\n" | nc web1 8080
HTTP/1.0 200 Ok
Content-Length: 14
Response File
Proxy
サーバー側
ポート 8080 を、localhost の 80 へプロキシ。
root@446c957cfaef:~# mkfifo backpipe
root@446c957cfaef:~# nc -l 8080 0<backpipe | nc localhost 80 1>backpipe
パイプでくっつけると、8080 にアクセスした相手へレスポンスを返さなくなるので、backpipe なる名前付きパイプを経由させます。
正直、mkfifo
コマンドなるものを初めて知ったので、あまり深く理解できていません。
完全にWikiをコピペしてます。すみません。
クライアント側
HTTP リクエストをしてみる。
root@9bd49622dea9:/# (echo -en "GET / HTTP/1.1\n\n"; sleep 1) | nc web1 8080
HTTP/1.1 400 Bad Request
Date: Mon, 22 Sep 2014 17:25:32 GMT
Server: Apache/2.4.7 (Ubuntu)
Content-Length: 299
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.7 (Ubuntu) Server at 10.1.0.3 Port 80</address>
</body></html>
sleep が必要なのは、Redis にアクセスする時と同じく、レスポンスを取得する前に nc コマンドが終了してしまうからだと思います。
まとめ
長々と書きましたが、ポートスキャニングくらいしか使わなさそうな気がします...。
また、デフォルトだと、SSL や SSH といったセキュアな通信はできない子っぽいので、nc を ssh
や scp
代わりに使うのは止めた方がいいでしょう。
余談ですが、今回の動作検証は、Dockerで作成したUbuntuコンテナで行っています。Redisサーバーもです。
Client役のコンテナから接続できるよう --link
を指定して動かしています。便利ですね。
追記
AWSのSecurityGroup設定の確認のため、ncコマンドを使ったので追記します。
受信側EC2上で、nc -l -p 8080
(nc -l 80801) とし、
送信側EC2で、nc -vz 受信側 8080
といった感じで接続を試みます。
穴開けしてあるなら succeeded!
になるはずです。
-
AmazonLinux上で、ncが私の知っているncじゃない! と思い調べたら、BSD版のncでした。
BSD nc だと、-p オプションの意味が違い、-l オプションに待ち受けポートの値を記述します。 ↩