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
}
var packet_data []byte
packet_data := bytes.NewBuffer(nil)
{
_pw := bytes.NewBuffer(nil)
pw := protocol.NewWriter(_pw, 0x0)
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()
@ -58,9 +62,9 @@ func dump_packet(from_client bool, w *pcapgo.Writer, pk packet.Packet) {
&layers.IPv4{
SrcIP: src_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 {
log.Fatal(err)

2
go.mod
View File

@ -21,7 +21,7 @@ require (
//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

View File

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

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"sync"
"github.com/sandertv/gophertunnel/minecraft"
@ -35,11 +36,30 @@ type ProxyContext struct {
client *minecraft.Conn
listener *minecraft.Listener
}
type (
PacketCallback func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error)
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 {
var c1, c2 *minecraft.Conn
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) {
/*
if strings.HasSuffix(server_address, ".pcap") {
return create_replay_connection(server_address)
}
*/
if strings.HasSuffix(server_address, ".pcap") {
return create_replay_connection(ctx, log, server_address, onConnect, packetCB)
}
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
os.MkdirAll(skin_dir, 0755)
os.MkdirAll(skin_dir, 0o755)
if have_geometry {
if err := skin.WriteGeometry(path.Join(skin_dir, "geometry.json")); err != nil {
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 skin_player_counts = make(map[string]int)
var processed_skins = make(map[string]bool)
var (
skin_players = make(map[string]string)
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) {
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
processed_skins[name] = true
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.filter, "filter", "", "player name filter prefix")
}
func (c *SkinCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n"
}
@ -264,7 +267,7 @@ func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}
println("Connected")
println("Press ctrl+c to exit")
os.MkdirAll(out_path, 0755)
os.MkdirAll(out_path, 0o755)
for {
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) {
if server == "" { // no arg provided, interactive input
fmt.Printf("Enter Server: ")

View File

@ -109,13 +109,13 @@ var IngameCommands = map[string]IngameCommand{
func setnameCommand(w *WorldState, cmdline []string) bool {
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
}
func toggleVoid(w *WorldState, cmdline []string) bool {
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
}
@ -496,7 +496,7 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
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() {
w.SaveAndReset()
@ -509,9 +509,11 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
default:
t := time.NewTimer(1 * time.Second)
for range t.C {
err := w.proxy.client.WritePacket(&MAP_ITEM_PACKET)
if err != nil {
return
if w.proxy.client != nil {
err := w.proxy.client.WritePacket(&MAP_ITEM_PACKET)
if err != nil {
return
}
}
}
}
@ -550,7 +552,7 @@ func (w *WorldState) ProcessPacketServer(pk packet.Packet) packet.Packet {
case *packet.LevelChunk:
w.ProcessLevelChunk(pk)
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:
w.ProcessSubChunk(pk)
case *packet.AvailableCommands: