shadowsocks-rustを改造して特定ドメインへの通信を遮断してみる。

# はじめに
特定ドメインに対する通信を指定した時間以外をブロックしたかったのでShadowsocksを改造してブロック機能をつけました。
既存ソフトの改造したいなーという方の助けになればいいかと思います。


# 使い方のゴール
こんなふうにすると

* example.comへの通信は9時30分～21時37分まで許可されます
* github.comへの通信は13時50分～17時30分と、21時00分～翌日9時30分まで通信が許可されます

``` toml
[[domain_list]]
start_time = "09:30"
end_time = "21:37"
domain = "example.com"

[[domain_list]]
start_time = "13:50"
end_time = "17:30"
domain = "github.com"

[[domain_list]]
start_time = "21:00"
end_time = "09:30"
domain = "github.com"
```

# どこをどう直すか

https://github.com/segfo/shadowsocks-rust/commit/1a14c03aad5d60408232b757feecd96ac403308e#diff-595920c6999a8490c0f3cca3125e1a6edd9567e9b525ebd606dc1e4cc3565575

大体こういう感じのコードになりますが、見ても一発ではわからんと思うので説明していきます。
まず、ShadowSocksのコンポーネントを理解するところから始めます。

## Shadowsocksのコンポーネント
ローカルサーバが有って、その先にトンネル用のエージェントがあります。
Shadowsocksの利用方法としてローカルサーバを立てて、トンネル用のエージェントを外部の国に仕掛けることでVPNできるよという感じですね。
つまり、ローカルサーバに通信を引っ掛けるところと暗号化をする処理というのが含まれているというわけです。通信を止めるためならローカルサーバを改造すればいいだけっぽいのでローカルサーバをいじっていきます。

大体把握できたのであとはソースを読むだけなので端折りますが、以下のように理解できます。

ローカルサーバの実行ファイルに関するコンポーネント
crates/shadowsocks-service/src/local/
プロトコル/トンネリング処理に関するコンポーネント
crates/shadowsocks-service/src/local/http/
crates/shadowsocks-service/src/local/socks/server/socks4/
crates/shadowsocks-service/src/local/socks/server/socks5
crates/shadowsocks-service/src/local/tunnel/

とりあえずドメインブロックする条件を記載していきましょう。

https://github.com/segfo/shadowsocks-rust/blob/1a14c03aad5d60408232b757feecd96ac403308e/crates/shadowsocks-service/src/local/domain_bloacker.rs

要点だけまとめると、domain_bloackerはドメインをブロックする条件をいい感じに計算してここはブロックするタイミングだよーと言うのを教えてくれる部分なので一旦全体像だけ把握しておいてください。

# 通信をインターセプトできる部分を探す
次に、ブロックしたい通信はHTTP・SOCKS（4と5）なのでそれぞれの通信にインターセプトできる部分を探していきます。

まずは簡単なHTTPから行きます。HTTPS通信はProxyする時CONNECTメソッドで接続先を指定してきます。その部分を狙ってインターセプトすれば通信拒否というのを実装できそうです。HTTPディスパッチャ（dispatcher.rs）という命名のソースがあるのでそこを見ていきます。

https://github.com/segfo/shadowsocks-rust/blob/1a14c03aad5d60408232b757feecd96ac403308e/crates/shadowsocks-service/src/local/http/dispatcher.rs#L99

というわけでここに、先程作ったdomain_bloackerのallow_accessを呼び出すことでいい感じにブロックしてくれます。ブロックするとErrorを返すようになるので上位にResultを返却することでプログラムを止めます。

同じ要領でSOCKS4とSOCKS5の通信についてもインターセプトしていきます。
SOCKSはトンネル内でDNS名解決する場合に限りDNS名（ホスト名）を利用できます。とりあえず今は細かいことを気にせずそういうものなんだなと思っておいてください。

SOCKS4とSOCK5ともに、トンネルにパケットをリレーするだけなのと仕組み的にはほぼ変わらないのでソース上の記述もほぼ変わりません。ソースもそのままrelayという風になっているので見つけ出していきましょう。

同じようにConnectのところで接続を処理するようなのでインターセプトしましょう。

https://github.com/segfo/shadowsocks-rust/blob/1a14c03aad5d60408232b757feecd96ac403308e/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs#L69

TcpConnectになっていますが、基本は同じです。インターセプトしていきましょう。

https://github.com/segfo/shadowsocks-rust/blob/1a14c03aad5d60408232b757feecd96ac403308e/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs#L218

UDPの方もとりあえずやっときましょう

https://github.com/segfo/shadowsocks-rust/blob/1a14c03aad5d60408232b757feecd96ac403308e/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs#L227

余談：クライアントでDNS名をトンネル内に通すという設定になっていない場合は素通りするため、どうしてもブロックしたい通信がある場合はDNSクエリをSOCKSサーバに確実に通すポリシとかを設定しないといけません。


# まとめ
基本的にプロトコルの知識があればソースを読むだけなので改造は容易にできる。
プロトコルを知りましょう。
Rustはいいぞ～

まぁぶっちゃけるとネタが無かったので過去やったことを引っ張り出してきただけですｗ

とりあえずあれです、内容を理解したければ何をやっているか（この場合はプロトコル）を理解することがまずは第一で、改造は二の次かなと思いました。

おわり