DNS (ドメインネームシステム)は、ドメイン情報を取得するための分散コンピューターシステムです。
TLS ( トランスポート層セキュリティプロトコル)-インターネットノード間の安全なデータ転送を提供します。
「Google Public DNSがDNS over TLSサポートを静かにオンにした」というニュースの後、私は試してみることにしました。 暗号化されたTCPトンネルを作成するトンネルがあります。 ただし、プログラムは通常UDPプロトコルを使用してDNSと通信します。 したがって、TCPストリームとの間でUDPパケットを転送するプロキシが必要です。 Luaに書き込みます。
TCPとUDP DNSパケットの完全な違い:
4.2.2。 TCPの使用
TCP接続を介して送信されるメッセージは、サーバーポート53(10進数)を使用します。 メッセージには、2バイトの長さフィールドを除く、メッセージの長さを示す2バイトの長さフィールドがプレフィックスとして付けられます。 この長さフィールドを使用すると、低レベル処理で解析を開始する前に完全なメッセージを組み立てることができます。
つまり、そこで行うことです。
- UDPからパケットを取得します
- このパケットのサイズが示されている数バイトを最初に追加します
- TCPチャネルに送信
そして反対方向に:
- TCPから数バイトを読み取り、パケットサイズを取得します
- TCPからパケットを読み取ります
- UDP経由で受信者に送信する
Stunnelをカスタマイズする
- Stunnel設定のあるディレクトリにルート証明書Root-R2.crtをダウンロードします
- 証明書をPEMに変換する
openssl x509 -inform DER -in Root-R2.crt -out Root-R2.pem -text
stunnel.confに書き込みます。
[dns] client = yes accept = 127.0.0.1:53 connect = 8.8.8.8:853 CAfile = Root-R2.pem verifyChain = yes checkIP = 8.8.8.8
つまり、Stunnel:
- 127.0.0.1:53で暗号化されていないTCPを受け入れます
- 8.8.8.8:853(Google DNS)のアドレスに暗号化されたTLSトンネルを開きます
- データをやり取りします
Stunnelを起動する
トンネルの動作は、次のコマンドで確認できます。
nslookup -vc ya.ru 127.0.0.1
-vcオプションは、nslookupがUDPの代わりにDNSサーバーへのTCP接続を使用するように強制します。
結果:
*** Can't find server name for address 127.0.0.1: Non-existent domain Server: UnKnown Address: 127.0.0.1 Non-authoritative answer: Name: ya.ru Address: ( IP )
スクリプトを書く
私はLua 5.3で書いています。 数値を使用したバイナリ演算はすでに利用可能です。 さて、 Lua Socketモジュールが必要になります。
ファイル名: simple-udp-to-tcp-dns-proxy.lua
local socket = require "socket" -- lua socket
--[[--
コンソールにダンプパッケージを送信できるようにする簡単な関数を作成しましょう。 プロキシが何をするのか見たいです。
--]]--
function serialize(data) -- az 0-9 HEX '\xFF' return "'"..data:gsub("[^a-z0-9-]", function(chr) return ("\\x%02X"):format(chr:byte()) end).."'" end
--[[--
UDPからTCPへ、およびその逆
2つのデータ伝送チャネルで動作する2つの関数を作成します。
--]]--
-- UDP TCP function udp_to_tcp_coroutine_function(udp_in, tcp_out, clients) repeat coroutine.yield() -- packet, err_ip, port = udp_in:receivefrom() -- UDP if packet then -- > - big endian -- I - unsigned integer -- 2 - 2 bytes size tcp_out:send(((">I2"):pack(#packet))..packet) -- TCP local id = (">I2"):unpack(packet:sub(1,2)) -- ID if not clients[id] then clients[id] = {} end table.insert(clients[id] ,{ip=err_ip, port=port, packet=packet}) -- print(os.date("%c", os.time()) ,err_ip, port, ">", serialize(packet)) -- end until false end -- TCP UDP function tcp_to_udp_coroutine_function(tcp_in, udp_out, clients) repeat coroutine.yield() -- -- > - big endian -- I - unsigned integer -- 2 - 2 bytes size local packet = tcp_in:receive((">I2"):unpack(tcp_in:receive(2)), nil) -- TCP local id = (">I2"):unpack(packet:sub(1,2)) -- ID if clients[id] then for key, client in pairs(clients[id]) do -- query if packet:find(client.packet:sub(13, -1), 13, true) == 13 then -- udp_out:sendto(packet, client.ip, client.port) -- UDP clients[id][key] = nil -- -- print(os.date("%c", os.time()) ,client.ip, client.port, "<", serialize(packet)) break end end if not next(clients[id]) then clients[id] = nil end end until false end
--[[--
両方の関数は、起動直後にcoroutine.yield()を実行します。 これにより、関数のパラメーターを最初の呼び出しに渡し、追加パラメーターなしでcoroutine.resume(co)を実行できます。
メイン
そして、メインループを準備して開始するメイン関数です。
--]]--
function main() local tcp_dns_socket = socket.tcp() -- TCP local udp_dns_socket = socket.udp() -- UDP local tcp_connected, err = tcp_dns_socket:connect("127.0.0.1", 53) -- TCP assert(tcp_connected, err) -- print("tcp dns connected") -- local udp_open, err = udp_dns_socket:setsockname("127.0.0.1", 53) -- UDP assert(udp_open, err) -- print("udp dns port open") -- UDP -- Lua nil -- local coroutines = { [tcp_dns_socket] = coroutine.create(tcp_to_udp_coroutine_function), -- TCP to UDP [udp_dns_socket] = coroutine.create(udp_to_tcp_coroutine_function) -- UDP to TCP } local clients = {} -- -- coroutine.resume(coroutines[tcp_dns_socket], tcp_dns_socket, udp_dns_socket, clients) coroutine.resume(coroutines[udp_dns_socket], udp_dns_socket, tcp_dns_socket, clients) -- socket.select local socket_list = {tcp_dns_socket, udp_dns_socket} repeat -- -- socket.select socket_list -- . for for _, in_socket in ipairs(socket.select(socket_list)) do -- local ok, err = coroutine.resume(coroutines[in_socket]) if not ok then -- udp_dns_socket:close() -- UDP tcp_dns_socket:close() -- TCP print(err) -- return -- end end until false end
--[[--
メイン関数を起動します。 接続が突然閉じられた場合、1秒後にmainを呼び出して再び接続を確立します。
--]]--
repeat local ok, err = coroutine.resume(coroutine.create(main)) -- main if not ok then print(err) end socket.sleep(1) -- until false
確認する
スタンネルを実行する
スクリプトを実行する
lua5.3 simple-udp-to-tcp-dns-proxy.lua
コマンドでスクリプトの動作を確認してください
nslookup ya.ru 127.0.0.1
今回は「-vc」を使用せずに、これがすでにUDPリクエストをTCPトンネルにラップするプロキシを作成および開始した方法です。
結果:
*** Can't find server name for address 127.0.0.1: Non-existent domain Server: UnKnown Address: 127.0.0.1 Non-authoritative answer: Name: ya.ru Address: ( IP )
すべてが正常であれば、接続設定でDNSサーバー「127.0.0.1」として指定できます。
結論
PS私たちはグーグルに追加情報を提供しません
接続直後、Windowsはトンネルを介してGoogleのDNSサーバーに登録しようとします。 これは、チェックを外すことにより、詳細なDNS設定で無効になります。
time.windows.comへのリクエストがまだあります。 彼はもうそれほど個人的ではありませんが、私はそれをオフにする方法を見つけたことがありません。 自動時刻同期は無効になっています。