add replay proxy
This commit is contained in:
parent
4a9451b7f0
commit
d770b979f1
12
capture.go
12
capture.go
|
@ -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
2
go.mod
|
@ -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
|
||||
|
||||
|
|
17
map_item.go
17
map_item.go
|
@ -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) {
|
||||
|
|
28
proxy.go
28
proxy.go
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
skins.go
15
skins.go
|
@ -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()
|
||||
|
|
14
utils.go
14
utils.go
|
@ -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: ")
|
||||
|
|
16
world.go
16
world.go
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue