プロキシ・MITM (中間者)
gophertunnel の最も強力な活用例のひとつが、クライアントとリモートサーバーの間に立つプロキシ(中間者/MITM) です。
プロキシを使うことで、クライアントとサーバーがやり取りする生のパケットをリアルタイムに監視、フィルタリング、書き換え、インジェクション(差し込み送信)できます。
プロキシの基本構造
透過プロキシのサンプル実装
以下は、クライアントからの接続を受け取り、リモートサーバー(例: 127.0.0.1:19133)にリレーするプロキシの骨組みです。
go
package main
import (
"errors"
"log"
"sync"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
func main() {
// プロキシの待受設定
lc := minecraft.ListenConfig{
AuthenticationDisabled: true,
}
listener, err := lc.Listen("raknet", "0.0.0.0:19132")
if err != nil {
log.Fatalf("プロキシ起動失敗: %v", err)
}
defer listener.Close()
log.Println("プロキシが待機中: 0.0.0.0:19132")
for {
clientConn, err := listener.Accept()
if err != nil {
log.Printf("接続受付エラー: %v", err)
continue
}
go handleProxy(clientConn.(*minecraft.Conn))
}
}
func handleProxy(clientConn *minecraft.Conn) {
defer clientConn.Close()
// クライアントの身元・認証情報を引き継いでリモートサーバーへ接続
dialer := minecraft.Dialer{
ClientData: clientConn.ClientData(),
IdentityData: clientConn.IdentityData(),
}
serverConn, err := dialer.Dial("raknet", "127.0.0.1:19133")
if err != nil {
log.Printf("リモートサーバー接続失敗: %v", err)
return
}
defer serverConn.Close()
// リモートサーバーから受け取ったGameDataをクライアント側に渡してゲーム開始
if err := clientConn.StartGame(serverConn.GameData()); err != nil {
log.Printf("StartGame転送失敗: %v", err)
return
}
var wg sync.WaitGroup
wg.Add(2)
// 1. クライアント -> サーバー のパケット転送
go func() {
defer wg.Done()
for {
pk, err := clientConn.ReadPacket()
if err != nil {
break
}
// ここで特定のパケットを検査・改変可能
// 例: チャットメッセージの傍受
if textPk, ok := pk.(*packet.Text); ok {
log.Printf("[Client Chat] %s: %s", textPk.SourceName, textPk.Message)
}
if err := serverConn.WritePacket(pk); err != nil {
break
}
}
}()
// 2. サーバー -> クライアント のパケット転送
go func() {
defer wg.Done()
for {
pk, err := serverConn.ReadPacket()
if err != nil {
break
}
if err := clientConn.WritePacket(pk); err != nil {
break
}
}
}()
wg.Wait()
log.Println("プロキシセッション終了")
}パケットの書き換えとドロップ
パケット転送ループ内で条件分岐を行うことで、特定のアクションを簡単に阻止または改変できます。
パケットのドロップ(遮断)
転送先の WritePacket を呼ばずに continue することで、パケットが相手に届かなくなります。
go
// クライアントがブロックを壊す操作パケットをキャンセルする例
if actionPk, ok := pk.(*packet.PlayerAction); ok {
if actionPk.ActionType == packet.PlayerActionStartBreak {
log.Println("ブロック破壊操作をキャンセルしました")
continue // サーバーに送らない
}
}パケットのインジェクション(割り込み送信)
相手側に独自のパケットを差し込んで送信することも可能です。
go
// サーバーからチャットパケットを受け取った際、クライアント側に偽のシステムメッセージを差し込む
clientConn.WritePacket(&packet.Text{
TextType: packet.TextTypeRaw,
Message: "§e[Proxy] パケットを検知しました",
SourceName: "",
})