add replay proxy

This commit is contained in:
olebeck 2022-09-04 00:56:40 +02:00
parent 4a9451b7f0
commit d770b979f1
8 changed files with 160 additions and 44 deletions

View File

@ -43,12 +43,16 @@ func dump_packet(from_client bool, w *pcapgo.Writer, pk packet.Packet) {
dst_ip = SrcIp_client dst_ip = SrcIp_client
} }
var packet_data []byte packet_data := bytes.NewBuffer(nil)
{ {
_pw := bytes.NewBuffer(nil) _pw := bytes.NewBuffer(nil)
pw := protocol.NewWriter(_pw, 0x0) pw := protocol.NewWriter(_pw, 0x0)
pk.Marshal(pw) pk.Marshal(pw)
packet_data = _pw.Bytes() h := packet.Header{
PacketID: pk.ID(),
}
h.Write(packet_data)
packet_data.Write(_pw.Bytes())
} }
serialize_buf := gopacket.NewSerializeBuffer() serialize_buf := gopacket.NewSerializeBuffer()
@ -58,9 +62,9 @@ func dump_packet(from_client bool, w *pcapgo.Writer, pk packet.Packet) {
&layers.IPv4{ &layers.IPv4{
SrcIP: src_ip, SrcIP: src_ip,
DstIP: dst_ip, DstIP: dst_ip,
Length: uint16(len(packet_data)), Length: uint16(packet_data.Len()),
}, },
gopacket.Payload(packet_data), gopacket.Payload(packet_data.Bytes()),
) )
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

2
go.mod
View File

@ -21,7 +21,7 @@ require (
//replace github.com/df-mc/dragonfly => ./dragonfly //replace github.com/df-mc/dragonfly => ./dragonfly
replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.24.8-3 replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.24.8-4
replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.8.3-0.20220902161600-2f9b3652bbb7 replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.8.3-0.20220902161600-2f9b3652bbb7

View File

@ -181,13 +181,16 @@ func (m *MapUI) Send(w *WorldState) error {
} }
m.send_lock.Unlock() m.send_lock.Unlock()
return w.proxy.client.WritePacket(&packet.ClientBoundMapItemData{ if w.proxy.client != nil {
MapID: VIEW_MAP_ID, return w.proxy.client.WritePacket(&packet.ClientBoundMapItemData{
Width: 128, MapID: VIEW_MAP_ID,
Height: 128, Width: 128,
Pixels: img2rgba(m.img), Height: 128,
UpdateFlags: 2, Pixels: img2rgba(m.img),
}) UpdateFlags: 2,
})
}
return nil
} }
func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk) { func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk) {

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"strings"
"sync" "sync"
"github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft"
@ -35,11 +36,30 @@ type ProxyContext struct {
client *minecraft.Conn client *minecraft.Conn
listener *minecraft.Listener listener *minecraft.Listener
} }
type ( type (
PacketCallback func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) PacketCallback func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error)
ConnectCallback func(proxy *ProxyContext) ConnectCallback func(proxy *ProxyContext)
) )
func (p *ProxyContext) sendMessage(text string) {
if p.client != nil {
p.client.WritePacket(&packet.Text{
TextType: packet.TextTypeSystem,
Message: "§8[§bBedrocktool§8]§r " + text,
})
}
}
func (p *ProxyContext) sendPopup(text string) {
if p.client != nil {
p.client.WritePacket(&packet.Text{
TextType: packet.TextTypePopup,
Message: text,
})
}
}
func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCBs []PacketCallback) error { func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCBs []PacketCallback) error {
var c1, c2 *minecraft.Conn var c1, c2 *minecraft.Conn
if toServer { if toServer {
@ -79,11 +99,9 @@ func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCB
} }
func create_proxy(ctx context.Context, log *logrus.Logger, server_address string, onConnect ConnectCallback, packetCB PacketCallback) (err error) { func create_proxy(ctx context.Context, log *logrus.Logger, server_address string, onConnect ConnectCallback, packetCB PacketCallback) (err error) {
/* if strings.HasSuffix(server_address, ".pcap") {
if strings.HasSuffix(server_address, ".pcap") { return create_replay_connection(ctx, log, server_address, onConnect, packetCB)
return create_replay_connection(server_address) }
}
*/
GetTokenSource() // ask for login before listening GetTokenSource() // ask for login before listening

100
replay.go Normal file
View File

@ -0,0 +1,100 @@
package main
import (
"context"
"fmt"
"os"
"reflect"
"time"
"unsafe"
"github.com/google/gopacket"
"github.com/google/gopacket/pcapgo"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sirupsen/logrus"
)
func SetUnexportedField(field reflect.Value, value interface{}) {
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
Elem().
Set(reflect.ValueOf(value))
}
type PayloadDecoder struct {
Payload []byte
}
func (d PayloadDecoder) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
return nil
}
func create_replay_connection(ctx context.Context, log *logrus.Logger, filename string, onConnect ConnectCallback, packetCB PacketCallback) error {
fmt.Printf("Reading replay %s\n", filename)
f, err := os.Open(filename)
if err != nil {
return err
}
reader, err := pcapgo.NewReader(f)
if err != nil {
return err
}
SetUnexportedField(reflect.ValueOf(reader).Elem().Field(5), uint32(0xFFFFFFFF))
dummy_conn := minecraft.NewConn()
dummy_conn.SetGameData(minecraft.GameData{
BaseGameVersion: "1.17.40", // SPECIFIC TO THE SERVER; TODO
})
proxy := ProxyContext{}
proxy.server = dummy_conn
if onConnect != nil {
onConnect(&proxy)
}
/* FOR OLD BROKEN CAPTURES
fake_head := packet.Header{
PacketID: packet.IDLevelChunk,
}
fake_header_w := bytes.NewBuffer(nil)
fake_head.Write(fake_header_w)
fake_header := fake_header_w.Bytes()
*/
start := time.Time{}
for {
data, ci, err := reader.ReadPacketData()
if err != nil {
return err
}
if start.Unix() == 0 {
start = ci.Timestamp
}
payload := data[0x14:]
if len(payload) == 0 {
continue
}
// payload = append(fake_header, payload...)
pk_data, err := minecraft.ParseData(payload, dummy_conn)
if err != nil {
return err
}
pks, err := pk_data.Decode(dummy_conn)
if err != nil {
log.Error(err)
continue
}
for _, pk := range pks {
if data[0x10] == 127 { // to client
packetCB(pk, &proxy, false)
} else {
packetCB(pk, &proxy, true)
}
}
}
}

View File

@ -129,7 +129,7 @@ func (skin *Skin) Write(output_path, name string) error {
have_geometry, have_cape, have_animations, have_tint := len(skin.SkinGeometry) > 0, len(skin.CapeData) > 0, len(skin.Animations) > 0, len(skin.PieceTintColours) > 0 have_geometry, have_cape, have_animations, have_tint := len(skin.SkinGeometry) > 0, len(skin.CapeData) > 0, len(skin.Animations) > 0, len(skin.PieceTintColours) > 0
os.MkdirAll(skin_dir, 0755) os.MkdirAll(skin_dir, 0o755)
if have_geometry { if have_geometry {
if err := skin.WriteGeometry(path.Join(skin_dir, "geometry.json")); err != nil { if err := skin.WriteGeometry(path.Join(skin_dir, "geometry.json")); err != nil {
return err return err
@ -188,9 +188,11 @@ func write_skin(output_path, name string, skin protocol.Skin, filter string) {
} }
} }
var skin_players = make(map[string]string) var (
var skin_player_counts = make(map[string]int) skin_players = make(map[string]string)
var processed_skins = make(map[string]bool) skin_player_counts = make(map[string]int)
processed_skins = make(map[string]bool)
)
func process_packet_skins(conn *minecraft.Conn, out_path string, pk packet.Packet, filter string) { func process_packet_skins(conn *minecraft.Conn, out_path string, pk packet.Packet, filter string) {
switch _pk := pk.(type) { switch _pk := pk.(type) {
@ -218,7 +220,7 @@ func process_packet_skins(conn *minecraft.Conn, out_path string, pk packet.Packe
skin_players[player.UUID.String()] = name skin_players[player.UUID.String()] = name
processed_skins[name] = true processed_skins[name] = true
if conn != nil { if conn != nil {
send_popup(conn, fmt.Sprintf("%s Skin was Saved", name)) (&ProxyContext{client: conn}).sendPopup(fmt.Sprintf("%s Skin was Saved", name))
} }
} }
} }
@ -236,6 +238,7 @@ func (c *SkinCMD) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.server_address, "address", "", "remote server address") f.StringVar(&c.server_address, "address", "", "remote server address")
f.StringVar(&c.filter, "filter", "", "player name filter prefix") f.StringVar(&c.filter, "filter", "", "player name filter prefix")
} }
func (c *SkinCMD) Usage() string { func (c *SkinCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n" return c.Name() + ": " + c.Synopsis() + "\n"
} }
@ -264,7 +267,7 @@ func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}
println("Connected") println("Connected")
println("Press ctrl+c to exit") println("Press ctrl+c to exit")
os.MkdirAll(out_path, 0755) os.MkdirAll(out_path, 0o755)
for { for {
pk, err := serverConn.ReadPacket() pk, err := serverConn.ReadPacket()

View File

@ -39,20 +39,6 @@ const SERVER_ADDRESS_HELP = `accepted server address formats:
` `
func send_popup(conn *minecraft.Conn, text string) {
conn.WritePacket(&packet.Text{
TextType: packet.TextTypePopup,
Message: text,
})
}
func send_message(conn *minecraft.Conn, text string) {
conn.WritePacket(&packet.Text{
TextType: packet.TextTypeSystem,
Message: "§8[§bBedrocktool§8]§r " + text,
})
}
func server_input(server string) (address, name string, err error) { func server_input(server string) (address, name string, err error) {
if server == "" { // no arg provided, interactive input if server == "" { // no arg provided, interactive input
fmt.Printf("Enter Server: ") fmt.Printf("Enter Server: ")

View File

@ -109,13 +109,13 @@ var IngameCommands = map[string]IngameCommand{
func setnameCommand(w *WorldState, cmdline []string) bool { func setnameCommand(w *WorldState, cmdline []string) bool {
w.WorldName = strings.Join(cmdline, " ") w.WorldName = strings.Join(cmdline, " ")
send_message(w.proxy.client, fmt.Sprintf("worldName is now: %s", w.WorldName)) w.proxy.sendMessage(fmt.Sprintf("worldName is now: %s", w.WorldName))
return true return true
} }
func toggleVoid(w *WorldState, cmdline []string) bool { func toggleVoid(w *WorldState, cmdline []string) bool {
w.voidgen = !w.voidgen w.voidgen = !w.voidgen
send_message(w.proxy.client, fmt.Sprintf("using void generator: %t", w.voidgen)) w.proxy.sendMessage(fmt.Sprintf("using void generator: %t", w.voidgen))
return true return true
} }
@ -496,7 +496,7 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
w.Dim = dimension_ids[uint8(dim_id)] w.Dim = dimension_ids[uint8(dim_id)]
} }
send_message(w.proxy.client, "use /setname <worldname>\nto set the world name") w.proxy.sendMessage("use /setname <worldname>\nto set the world name")
G_exit = append(G_exit, func() { G_exit = append(G_exit, func() {
w.SaveAndReset() w.SaveAndReset()
@ -509,9 +509,11 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
default: default:
t := time.NewTimer(1 * time.Second) t := time.NewTimer(1 * time.Second)
for range t.C { for range t.C {
err := w.proxy.client.WritePacket(&MAP_ITEM_PACKET) if w.proxy.client != nil {
if err != nil { err := w.proxy.client.WritePacket(&MAP_ITEM_PACKET)
return if err != nil {
return
}
} }
} }
} }
@ -550,7 +552,7 @@ func (w *WorldState) ProcessPacketServer(pk packet.Packet) packet.Packet {
case *packet.LevelChunk: case *packet.LevelChunk:
w.ProcessLevelChunk(pk) w.ProcessLevelChunk(pk)
w.ui.Send(w) w.ui.Send(w)
send_popup(w.proxy.client, fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName)) w.proxy.sendPopup(fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName))
case *packet.SubChunk: case *packet.SubChunk:
w.ProcessSubChunk(pk) w.ProcessSubChunk(pk)
case *packet.AvailableCommands: case *packet.AvailableCommands: