nc コマンド 使い方メモ

# はじめに

nc コマンドを使いこなせれば、ネットワークに関するおおよその調査ができると教わったので、使い方をメモしてみます。

そもそものきっかけは、dockerの公式Imageから作ったUbuntuコンテナが、デフォルトだと`wget`も`telnet`もインストールできなかったからです。
nc はデフォルトで入っていたので、これを機にまとめることにしました。

おおよそ Wikipedia と man を参考にして書いています。
使用例は http://www.computerhope.com/unix/nc.htm も参考になりそうです。

# 簡易まとめ表

| コマンド | 用途 |
|:--------|:----|
|echo -en "GET / HTTP/1.1\n\n" &#124; nc localhost  80|HTTP GET リクエスト|
|nc -zv localhost 1-65535|ポートスキャニング|
|(echo 'set KEY1 hoge'; sleep 1s; echo 'key *') &#124; nc localhost 6379 |Redisサーバーにアクセス|
|while : ; do (echo -ne "HTTP/1.0 200 Ok\nContent-Length: $(wc -c < response.txt)\n\n"; cat response.txt) &#124; nc -l -p 8080; done|Mockサーバー|
|nc -l 8080 0\<backpipe &#124; nc localhost 80 1\>backpip|Proxy|
|/bin/nc.openbsd -x proxy_host:1080 %h %p|Socksサーバー経由でSSH接続用(BSD版に限る)|

# Webサーバーに問い合わせ
## GETリクエストを投げる
```bash
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&amp;ei=pSYgVIaBEMyT8QfJooHADw">here</A>.
</BODY></HTML>
```

### レスポンスコードのみ出力
```bash
root@abd32f2b7776:/# echo -en "GET / HTTP/1.1\n\n" | nc www.google.com 80 | head -n 1
HTTP/1.1 302 Found
```
`head` コマンドを使っているだけです。

## 存在しないサーバーを指定した場合
```bash
root@abd32f2b7776:/# echo -en "GET / HTTP/1.1\n\n" | nc noexist 80
nc: getaddrinfo: Name or service not known
```

## Listenしてないポートを指定した場合
```bash
root@9bd49622dea9:/# echo -en "GET / HTTP/1.1\n\n" | nc web1 8080
root@9bd49622dea9:/# echo $?
1
```
なにも出力されないので、終了コードを見るしかないようです。

# Port Scanning
```bash
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 サーバーにアクセス
```bash
root@9bd49622dea9:/# nc redis 6379
set key Hello
+OK
get key
$5
Hello
del key
:1
keys *
*0
quit
+OK
```
`telnet` と同じようことができます。

## ワンライナー版

```bash
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状態にしておきます。

```bash
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回目以降はレスポンスが得られません。

## クライアント側

```bash
root@9bd49622dea9:/# nc web1 8080
HTTP/1.0 200 Ok
Content-Length: 14

Response File
```
response.txt を相応しいものにしておけば、簡単なMockサーバーを用意できそうですね。
もちろん、GETリクエストでも同じレスポンスが得られます。

```bash
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 へプロキシ。

```bash
root@446c957cfaef:~# mkfifo backpipe
root@446c957cfaef:~# nc -l 8080 0<backpipe | nc localhost 80 1>backpipe
```

パイプでくっつけると、8080 にアクセスした相手へレスポンスを返さなくなるので、backpipe なる名前付きパイプを経由させます。
正直、`mkfifo` コマンドなるものを初めて知ったので、あまり深く理解できていません。
完全にWikiをコピペしてます。すみません。

## クライアント側
HTTP リクエストをしてみる。

```bash

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 8080[^1]) とし、
送信側EC2で、`nc -vz 受信側 8080` といった感じで接続を試みます。


穴開けしてあるなら `succeeded!` になるはずです。

[^1]: AmazonLinux上で、ncが私の知っているncじゃない！ と思い調べたら、BSD版のncでした。
BSD nc だと、-p オプションの意味が違い、-l オプションに待ち受けポートの値を記述します。