make capture not produce broken files
This commit is contained in:
parent
7acc698443
commit
5d099729ae
50
capture.go
50
capture.go
|
@ -8,14 +8,12 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcapgo"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -29,41 +27,24 @@ func init() {
|
|||
register_command(&CaptureCMD{})
|
||||
}
|
||||
|
||||
func dump_packet(from_client bool, w *pcapgo.Writer, pk packet.Packet) {
|
||||
func dump_packet(w *pcapgo.Writer, from_client bool, pk packet.Header, payload []byte) {
|
||||
var err error
|
||||
var iface_index int
|
||||
var src_ip, dst_ip net.IP
|
||||
var prefix []byte
|
||||
if from_client {
|
||||
iface_index = 1
|
||||
src_ip = SrcIp_client
|
||||
dst_ip = SrcIp_server
|
||||
prefix = []byte("client")
|
||||
} else {
|
||||
iface_index = 2
|
||||
src_ip = SrcIp_server
|
||||
dst_ip = SrcIp_client
|
||||
prefix = []byte("server")
|
||||
}
|
||||
|
||||
packet_data := bytes.NewBuffer(nil)
|
||||
{
|
||||
_pw := bytes.NewBuffer(nil)
|
||||
pw := protocol.NewWriter(_pw, 0x0)
|
||||
pk.Marshal(pw)
|
||||
h := packet.Header{
|
||||
PacketID: pk.ID(),
|
||||
}
|
||||
h.Write(packet_data)
|
||||
packet_data.Write(_pw.Bytes())
|
||||
}
|
||||
pk.Write(packet_data)
|
||||
packet_data.Write(payload)
|
||||
|
||||
serialize_buf := gopacket.NewSerializeBuffer()
|
||||
err = gopacket.SerializeLayers(
|
||||
serialize_buf,
|
||||
gopacket.SerializeOptions{},
|
||||
&layers.IPv4{
|
||||
SrcIP: src_ip,
|
||||
DstIP: dst_ip,
|
||||
Length: uint16(packet_data.Len()),
|
||||
},
|
||||
gopacket.Payload(prefix),
|
||||
gopacket.Payload(packet_data.Bytes()),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -74,7 +55,7 @@ func dump_packet(from_client bool, w *pcapgo.Writer, pk packet.Packet) {
|
|||
Timestamp: time.Now(),
|
||||
Length: len(serialize_buf.Bytes()),
|
||||
CaptureLength: len(serialize_buf.Bytes()),
|
||||
InterfaceIndex: iface_index,
|
||||
InterfaceIndex: 1,
|
||||
}, serialize_buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -112,16 +93,15 @@ func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interfac
|
|||
w := pcapgo.NewWriter(fio)
|
||||
w.WriteFileHeader(65536, layers.LinkTypeEthernet)
|
||||
|
||||
_wl := sync.Mutex{}
|
||||
proxy := NewProxy(logrus.StandardLogger())
|
||||
proxy.packetFunc = func(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||
from_client := src.String() == proxy.client.LocalAddr().String()
|
||||
dump_packet(w, from_client, header, payload)
|
||||
}
|
||||
|
||||
err = create_proxy(ctx, logrus.StandardLogger(), address, nil, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
_wl.Lock()
|
||||
dump_packet(toServer, w, pk)
|
||||
_wl.Unlock()
|
||||
return pk, nil
|
||||
})
|
||||
err = proxy.Run(ctx, address)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
logrus.Error(err)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
|
|
12
main.go
12
main.go
|
@ -18,9 +18,11 @@ const TOKEN_FILE = "token.json"
|
|||
|
||||
var version string
|
||||
|
||||
var G_debug bool
|
||||
var G_preload_packs bool
|
||||
var G_exit []func() = []func(){}
|
||||
var (
|
||||
G_debug bool
|
||||
G_preload_packs bool
|
||||
G_exit []func() = []func(){}
|
||||
)
|
||||
|
||||
func exit() {
|
||||
logrus.Info("\nExiting\n")
|
||||
|
@ -40,7 +42,7 @@ func register_command(sub subcommands.Command) {
|
|||
func main() {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
if version != "" {
|
||||
logrus.Info("bedrocktool version: %s\n", version)
|
||||
logrus.Infof("bedrocktool version: %s\n", version)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
@ -108,6 +110,7 @@ func (*TransCMD) Synopsis() string { return "" }
|
|||
func (c *TransCMD) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&c.auth, "auth", false, "if it should login to xbox")
|
||||
}
|
||||
|
||||
func (c *TransCMD) Usage() string {
|
||||
return c.Name() + ": " + c.Synopsis() + "\n"
|
||||
}
|
||||
|
@ -127,6 +130,7 @@ func (c *TransCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
|||
fmt.Println(BLACK_FG + BOLD + BLUE + " Trans " + PINK + " Rights " + WHITE + " Are " + PINK + " Human " + BLUE + " Rights " + RESET)
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
register_command(&TransCMD{})
|
||||
}
|
||||
|
|
2
merge.go
2
merge.go
|
@ -73,7 +73,7 @@ func (c *MergeCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
|
|||
time.Sleep(1 * time.Second)
|
||||
|
||||
if err := zip_folder(out_name+".mcworld", out_name); err != nil {
|
||||
logrus.Info("zipping: %s", err)
|
||||
logrus.Infof("zipping: %s", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
74
proxy.go
74
proxy.go
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -37,9 +38,19 @@ type ProxyContext struct {
|
|||
client *minecraft.Conn
|
||||
listener *minecraft.Listener
|
||||
commands map[string]IngameCommand
|
||||
|
||||
log *logrus.Logger
|
||||
|
||||
// called for every packet
|
||||
packetFunc PacketFunc
|
||||
// called after game started
|
||||
onConnect ConnectCallback
|
||||
// called on every packet after login
|
||||
packetCB PacketCallback
|
||||
}
|
||||
|
||||
type (
|
||||
PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr)
|
||||
PacketCallback func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error)
|
||||
ConnectCallback func(proxy *ProxyContext)
|
||||
)
|
||||
|
@ -127,30 +138,37 @@ 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 NewProxy(log *logrus.Logger) *ProxyContext {
|
||||
if log == nil {
|
||||
log = logrus.StandardLogger()
|
||||
}
|
||||
return &ProxyContext{
|
||||
log: log,
|
||||
commands: make(map[string]IngameCommand),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProxyContext) Run(ctx context.Context, server_address string) (err error) {
|
||||
if strings.HasSuffix(server_address, ".pcap") {
|
||||
return create_replay_connection(ctx, log, server_address, onConnect, packetCB)
|
||||
return create_replay_connection(ctx, p.log, server_address, p.onConnect, p.packetCB)
|
||||
}
|
||||
|
||||
GetTokenSource() // ask for login before listening
|
||||
|
||||
proxy := ProxyContext{}
|
||||
proxy.commands = make(map[string]IngameCommand)
|
||||
|
||||
var packs []*resource.Pack
|
||||
if G_preload_packs {
|
||||
log.Info("Preloading resourcepacks")
|
||||
serverConn, err := connect_server(ctx, server_address, nil, true)
|
||||
p.log.Info("Preloading resourcepacks")
|
||||
serverConn, err := connect_server(ctx, server_address, nil, true, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to %s: %s", server_address, err)
|
||||
}
|
||||
serverConn.Close()
|
||||
packs = serverConn.ResourcePacks()
|
||||
log.Infof("%d packs loaded\n", len(packs))
|
||||
p.log.Infof("%d packs loaded\n", len(packs))
|
||||
}
|
||||
|
||||
_status := minecraft.NewStatusProvider("Server")
|
||||
proxy.listener, err = minecraft.ListenConfig{
|
||||
p.listener, err = minecraft.ListenConfig{
|
||||
StatusProvider: _status,
|
||||
ResourcePacks: packs,
|
||||
AcceptedProtocols: []minecraft.Protocol{
|
||||
|
@ -160,46 +178,48 @@ func create_proxy(ctx context.Context, log *logrus.Logger, server_address string
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer proxy.listener.Close()
|
||||
defer p.listener.Close()
|
||||
|
||||
log.Infof("Listening on %s\n", proxy.listener.Addr())
|
||||
p.log.Infof("Listening on %s\n", p.listener.Addr())
|
||||
|
||||
c, err := proxy.listener.Accept()
|
||||
c, err := p.listener.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
p.log.Fatal(err)
|
||||
}
|
||||
proxy.client = c.(*minecraft.Conn)
|
||||
p.client = c.(*minecraft.Conn)
|
||||
|
||||
cd := proxy.client.ClientData()
|
||||
proxy.server, err = connect_server(ctx, server_address, &cd, false)
|
||||
cd := p.client.ClientData()
|
||||
p.server, err = connect_server(ctx, server_address, &cd, false, p.packetFunc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to %s: %s", server_address, err)
|
||||
}
|
||||
|
||||
// spawn and start the game
|
||||
if err := spawn_conn(ctx, proxy.client, proxy.server); err != nil {
|
||||
if err := spawn_conn(ctx, p.client, p.server); err != nil {
|
||||
return fmt.Errorf("failed to spawn: %s", err)
|
||||
}
|
||||
|
||||
defer proxy.server.Close()
|
||||
defer proxy.listener.Disconnect(proxy.client, G_disconnect_reason)
|
||||
defer p.server.Close()
|
||||
defer p.listener.Disconnect(p.client, G_disconnect_reason)
|
||||
|
||||
if onConnect != nil {
|
||||
onConnect(&proxy)
|
||||
if p.onConnect != nil {
|
||||
p.onConnect(p)
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
cbs := []PacketCallback{
|
||||
proxy.CommandHandlerPacketCB,
|
||||
packetCB,
|
||||
p.CommandHandlerPacketCB,
|
||||
}
|
||||
if p.packetCB != nil {
|
||||
cbs = append(cbs, p.packetCB)
|
||||
}
|
||||
|
||||
// server to client
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := proxyLoop(ctx, &proxy, false, cbs); err != nil {
|
||||
log.Error(err)
|
||||
if err := proxyLoop(ctx, p, false, cbs); err != nil {
|
||||
p.log.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
@ -208,8 +228,8 @@ func create_proxy(ctx context.Context, log *logrus.Logger, server_address string
|
|||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := proxyLoop(ctx, &proxy, true, cbs); err != nil {
|
||||
log.Error(err)
|
||||
if err := proxyLoop(ctx, p, true, cbs); err != nil {
|
||||
p.log.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
|
77
replay.go
77
replay.go
|
@ -23,7 +23,7 @@ func SetUnexportedField(field reflect.Value, value interface{}) {
|
|||
func create_replay_connection(ctx context.Context, log *logrus.Logger, filename string, onConnect ConnectCallback, packetCB PacketCallback) error {
|
||||
log.Infof("Reading replay %s", filename)
|
||||
|
||||
OLD_BROKEN := true
|
||||
OLD_BROKEN := false
|
||||
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
|
@ -35,17 +35,8 @@ func create_replay_connection(ctx context.Context, log *logrus.Logger, filename
|
|||
}
|
||||
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)
|
||||
}
|
||||
proxy := NewProxy(logrus.StandardLogger())
|
||||
proxy.server = minecraft.NewConn()
|
||||
|
||||
var fake_header []byte
|
||||
if OLD_BROKEN {
|
||||
|
@ -58,6 +49,8 @@ func create_replay_connection(ctx context.Context, log *logrus.Logger, filename
|
|||
fake_header = fake_header_w.Bytes()
|
||||
}
|
||||
|
||||
game_started := false
|
||||
|
||||
start := time.Time{}
|
||||
for {
|
||||
data, ci, err := reader.ReadPacketData()
|
||||
|
@ -67,31 +60,73 @@ func create_replay_connection(ctx context.Context, log *logrus.Logger, filename
|
|||
if start.Unix() == 0 {
|
||||
start = ci.Timestamp
|
||||
}
|
||||
|
||||
payload := data[0x14:]
|
||||
if len(payload) == 0 {
|
||||
if len(data) < 0x14 {
|
||||
continue
|
||||
}
|
||||
|
||||
var payload []byte
|
||||
var toServer bool
|
||||
if OLD_BROKEN {
|
||||
payload = append(fake_header, payload...)
|
||||
payload = append(fake_header, data[0x14:]...)
|
||||
toServer = data[0x10] != 127
|
||||
} else {
|
||||
prefix := data[0:6]
|
||||
payload = data[6:]
|
||||
toServer = bytes.Equal(prefix, []byte("client"))
|
||||
}
|
||||
|
||||
pk_data, err := minecraft.ParseData(payload, dummy_conn)
|
||||
pk_data, err := minecraft.ParseData(payload, proxy.server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pks, err := pk_data.Decode(dummy_conn)
|
||||
pks, err := pk_data.Decode(proxy.server)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pk := range pks {
|
||||
if data[0x10] == 127 { // to client
|
||||
packetCB(pk, &proxy, false)
|
||||
if game_started || OLD_BROKEN {
|
||||
if packetCB != nil {
|
||||
packetCB(pk, proxy, toServer)
|
||||
}
|
||||
} else {
|
||||
packetCB(pk, &proxy, true)
|
||||
switch pk := pk.(type) {
|
||||
case *packet.StartGame:
|
||||
proxy.server.SetGameData(minecraft.GameData{
|
||||
WorldName: pk.WorldName,
|
||||
WorldSeed: pk.WorldSeed,
|
||||
Difficulty: pk.Difficulty,
|
||||
EntityUniqueID: pk.EntityUniqueID,
|
||||
EntityRuntimeID: pk.EntityRuntimeID,
|
||||
PlayerGameMode: pk.PlayerGameMode,
|
||||
PersonaDisabled: pk.PersonaDisabled,
|
||||
CustomSkinsDisabled: pk.CustomSkinsDisabled,
|
||||
BaseGameVersion: pk.BaseGameVersion,
|
||||
PlayerPosition: pk.PlayerPosition,
|
||||
Pitch: pk.Pitch,
|
||||
Yaw: pk.Yaw,
|
||||
Dimension: pk.Dimension,
|
||||
WorldSpawn: pk.WorldSpawn,
|
||||
EditorWorld: pk.EditorWorld,
|
||||
WorldGameMode: pk.WorldGameMode,
|
||||
GameRules: pk.GameRules,
|
||||
Time: pk.Time,
|
||||
ServerBlockStateChecksum: pk.ServerBlockStateChecksum,
|
||||
CustomBlocks: pk.Blocks,
|
||||
Items: pk.Items,
|
||||
PlayerMovementSettings: pk.PlayerMovementSettings,
|
||||
ServerAuthoritativeInventory: pk.ServerAuthoritativeInventory,
|
||||
Experiments: pk.Experiments,
|
||||
ClientSideGeneration: pk.ClientSideGeneration,
|
||||
ChatRestrictionLevel: pk.ChatRestrictionLevel,
|
||||
DisablePlayerInteractions: pk.DisablePlayerInteractions,
|
||||
})
|
||||
game_started = true
|
||||
if onConnect != nil {
|
||||
onConnect(proxy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,12 +37,15 @@ func (c *SkinProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interf
|
|||
out_path := fmt.Sprintf("skins/%s", hostname)
|
||||
os.MkdirAll(out_path, 0o755)
|
||||
|
||||
err = create_proxy(ctx, logrus.StandardLogger(), address, nil, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
proxy := NewProxy(logrus.StandardLogger())
|
||||
proxy.packetCB = func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
if !toServer {
|
||||
process_packet_skins(proxy.client, out_path, pk, c.filter)
|
||||
}
|
||||
return pk, nil
|
||||
})
|
||||
}
|
||||
|
||||
err = proxy.Run(ctx, address)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return 1
|
||||
|
|
2
skins.go
2
skins.go
|
@ -251,7 +251,7 @@ func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}
|
|||
return 1
|
||||
}
|
||||
|
||||
serverConn, err := connect_server(ctx, address, nil, false)
|
||||
serverConn, err := connect_server(ctx, address, nil, false, nil)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, err)
|
||||
return 1
|
||||
|
|
12
utils.go
12
utils.go
|
@ -110,10 +110,14 @@ func server_url_to_name(server string) string {
|
|||
return host
|
||||
}
|
||||
|
||||
func connect_server(ctx context.Context, address string, ClientData *login.ClientData, want_packs bool) (serverConn *minecraft.Conn, err error) {
|
||||
var packet_func func(header packet.Header, payload []byte, src, dst net.Addr) = nil
|
||||
if G_debug {
|
||||
packet_func = PacketLogger
|
||||
func connect_server(ctx context.Context, address string, ClientData *login.ClientData, want_packs bool, packetFunc PacketFunc) (serverConn *minecraft.Conn, err error) {
|
||||
packet_func := func(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||
if G_debug {
|
||||
PacketLogger(header, payload, src, dst)
|
||||
if packetFunc != nil {
|
||||
packetFunc(header, payload, src, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cd := login.ClientData{}
|
||||
|
|
8
world.go
8
world.go
|
@ -132,14 +132,18 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
|
|||
w.withPacks = c.packs
|
||||
w.ctx = ctx
|
||||
|
||||
err = create_proxy(ctx, logrus.StandardLogger(), server_address, w.OnConnect, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
proxy := NewProxy(logrus.StandardLogger())
|
||||
proxy.onConnect = w.OnConnect
|
||||
proxy.packetCB = func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
if toServer {
|
||||
pk = w.ProcessPacketClient(pk)
|
||||
} else {
|
||||
pk = w.ProcessPacketServer(pk)
|
||||
}
|
||||
return pk, nil
|
||||
})
|
||||
}
|
||||
|
||||
err = proxy.Run(ctx, server_address)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
|
|
Loading…
Reference in New Issue