mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-12 23:14:36 +01:00
Port entrance server to Go
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
package main
|
||||
package entranceserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
||||
"github.com/Andoryuuta/Erupe/network"
|
||||
@@ -31,17 +30,40 @@ func handleEntranceServerConnection(conn net.Conn) {
|
||||
|
||||
fmt.Printf("Got entrance server command:\n%s\n", hex.Dump(pkt))
|
||||
|
||||
data, err := ioutil.ReadFile("custom_entrance_server_resp.bin")//("tw_server_list_resp.bin")
|
||||
if err != nil {
|
||||
print(err)
|
||||
return
|
||||
}
|
||||
data := makeResp([]ServerInfo{
|
||||
ServerInfo{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Unk2: 0,
|
||||
Type: 1,
|
||||
Season: 0,
|
||||
Unk6: 3,
|
||||
Name: "AErupe Server in Go! @localhost",
|
||||
AllowedClientFlags: 4096,
|
||||
Channels: []ChannelInfo{
|
||||
ChannelInfo{
|
||||
Port: 54001,
|
||||
MaxPlayers: 100,
|
||||
CurrentPlayers: 3,
|
||||
Unk4: 0,
|
||||
Unk5: 0,
|
||||
Unk6: 0,
|
||||
Unk7: 0,
|
||||
Unk8: 0,
|
||||
Unk9: 0,
|
||||
Unk10: 319,
|
||||
Unk11: 248,
|
||||
Unk12: 159,
|
||||
Unk13: 12345,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
cc.SendPacket(data)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func doEntranceServer(listenAddr string) {
|
||||
func DoEntranceServer(listenAddr string) {
|
||||
l, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
119
entranceserver/make_resp.go
Normal file
119
entranceserver/make_resp.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package entranceserver
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
)
|
||||
|
||||
func paddedString(x string, size uint) []byte {
|
||||
out := make([]byte, size)
|
||||
copy(out, x)
|
||||
|
||||
// Null terminate it.
|
||||
out[len(out)-1] = 0
|
||||
return out
|
||||
}
|
||||
|
||||
// ServerInfo represents an entry in the serverlist.
|
||||
type ServerInfo struct {
|
||||
IP net.IP
|
||||
Unk2 uint16
|
||||
Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
|
||||
Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue
|
||||
Unk6 uint8 // Something to do with server recommendation on 0, 3, and 5.
|
||||
Name string // Server name, 66 byte null terminated Shift-JIS.
|
||||
|
||||
// 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing?
|
||||
// THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
|
||||
AllowedClientFlags uint32
|
||||
|
||||
Channels []ChannelInfo
|
||||
}
|
||||
|
||||
// ChannelInfo represents an entry in a server's channel list.
|
||||
type ChannelInfo struct {
|
||||
Port uint16
|
||||
//ChannelIndex uint16
|
||||
MaxPlayers uint16
|
||||
CurrentPlayers uint16
|
||||
Unk4 uint16
|
||||
Unk5 uint16
|
||||
Unk6 uint16
|
||||
Unk7 uint16
|
||||
Unk8 uint16
|
||||
Unk9 uint16
|
||||
Unk10 uint16
|
||||
Unk11 uint16
|
||||
Unk12 uint16
|
||||
Unk13 uint16
|
||||
}
|
||||
|
||||
func encodeServerInfo(serverInfos []ServerInfo) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
for serverIdx, si := range serverInfos {
|
||||
bf.WriteUint32(binary.LittleEndian.Uint32(si.IP))
|
||||
bf.WriteUint16(16 + uint16(serverIdx))
|
||||
bf.WriteUint16(si.Unk2)
|
||||
bf.WriteUint16(uint16(len(si.Channels)))
|
||||
bf.WriteUint8(si.Type)
|
||||
bf.WriteUint8(si.Season)
|
||||
bf.WriteUint8(si.Unk6)
|
||||
bf.WriteBytes(paddedString(si.Name, 66))
|
||||
bf.WriteUint32(si.AllowedClientFlags)
|
||||
|
||||
for channelIdx, ci := range si.Channels {
|
||||
bf.WriteUint16(ci.Port)
|
||||
bf.WriteUint16(16 + uint16(channelIdx))
|
||||
bf.WriteUint16(ci.MaxPlayers)
|
||||
bf.WriteUint16(ci.CurrentPlayers)
|
||||
bf.WriteUint16(ci.Unk4)
|
||||
bf.WriteUint16(ci.Unk5)
|
||||
bf.WriteUint16(ci.Unk6)
|
||||
bf.WriteUint16(ci.Unk7)
|
||||
bf.WriteUint16(ci.Unk8)
|
||||
bf.WriteUint16(ci.Unk9)
|
||||
bf.WriteUint16(ci.Unk10)
|
||||
bf.WriteUint16(ci.Unk11)
|
||||
bf.WriteUint16(ci.Unk12)
|
||||
bf.WriteUint16(ci.Unk13)
|
||||
}
|
||||
}
|
||||
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes([]byte(respType))
|
||||
bf.WriteUint16(entryCount)
|
||||
bf.WriteUint16(uint16(len(data)))
|
||||
if len(data) > 0 {
|
||||
bf.WriteUint32(CalcSum32(data))
|
||||
bf.WriteBytes(data)
|
||||
}
|
||||
|
||||
dataToEncrypt := bf.Data()
|
||||
|
||||
bf = byteframe.NewByteFrame()
|
||||
bf.WriteUint8(key)
|
||||
bf.WriteBytes(EncryptBin8(dataToEncrypt, key))
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func makeResp(servers []ServerInfo) []byte {
|
||||
rawServerData := encodeServerInfo(servers)
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(servers)), 0x00))
|
||||
|
||||
// TODO(Andoryuuta): Figure out what this user data is.
|
||||
// Is it for the friends list at the world selection screen?
|
||||
// If so, how does it work without the entrance server connection being authenticated?
|
||||
bf.WriteBytes(makeHeader([]byte{}, "USR", 0, 0x00))
|
||||
|
||||
return bf.Data()
|
||||
|
||||
}
|
||||
4
main.go
4
main.go
@@ -6,8 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Andoryuuta/Erupe/channelserver"
|
||||
_ "github.com/Andoryuuta/Erupe/network/mhfpacket"
|
||||
"github.com/Andoryuuta/Erupe/signserver"
|
||||
"github.com/Andoryuuta/Erupe/entranceserver"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ func main() {
|
||||
|
||||
// Finally start our server(s).
|
||||
go serveLauncherHTML(":80", false)
|
||||
go doEntranceServer(":53310")
|
||||
go entranceserver.DoEntranceServer(":53310")
|
||||
|
||||
signServer := signserver.NewServer(
|
||||
&signserver.Config{
|
||||
|
||||
225
test.py
225
test.py
@@ -1,225 +0,0 @@
|
||||
from hexdump import hexdump
|
||||
import io
|
||||
import sys
|
||||
|
||||
from construct import *
|
||||
|
||||
Binary8Header = Struct(
|
||||
"server_type" / Bytes(3),
|
||||
"entry_count" / Int16ub,
|
||||
"body_size" / Int16ub,
|
||||
"checksum" / Int32ub,
|
||||
)
|
||||
|
||||
EntranceListComplete = Struct(
|
||||
Embedded(Binary8Header),
|
||||
"servers" / Array(this.entry_count,
|
||||
Struct(
|
||||
"host_ip_4byte" / Int32ub,
|
||||
"unk_1" / Int16ub, # Server ID maybe?
|
||||
"unk_2" / Int16ub,
|
||||
"channel_count" / Int16ub,
|
||||
"server_type" / Byte, # Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
|
||||
"color" / Byte, # Server activity. 0 = green, 1 = orange, 2 = blue
|
||||
"unk_6" / Byte, # Something to do with server recommendation on 0, 3, and 5.
|
||||
"name" / Bytes(66), # Shift-JIS.
|
||||
|
||||
# 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing?
|
||||
# THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
|
||||
"allowed_client_type_flags" / Int32ub,
|
||||
|
||||
"channels" / Array(this.channel_count,
|
||||
Struct(
|
||||
"port" / Int16ub,
|
||||
"unk_1" / Int16ub, # Channel ID maybe?
|
||||
"max_players" / Int16ub,
|
||||
"current_players" / Int16ub,
|
||||
"unk_4" / Int16ub,
|
||||
"unk_5" / Int16ub,
|
||||
"unk_6" / Int16ub,
|
||||
"unk_7" / Int16ub,
|
||||
"unk_8" / Int16ub,
|
||||
"unk_9" / Int16ub,
|
||||
"unk_10" / Int16ub,
|
||||
"unk_11" / Int16ub,
|
||||
"unk_12" / Int16ub,
|
||||
"unk_13" / Int16ub,
|
||||
)
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
BINARY8_KEY = bytes([0x01, 0x23, 0x34, 0x45, 0x56, 0xAB, 0xCD, 0xEF])
|
||||
def decode_binary8(data, unk_key_byte):
|
||||
cur_key = ((54323 * unk_key_byte) + 1) & 0xFFFFFFFF
|
||||
|
||||
output_data = bytearray()
|
||||
for i in range(len(data)):
|
||||
tmp = (data[i] ^ (cur_key >> 13)) & 0xFF
|
||||
output_data.append(tmp ^ BINARY8_KEY[i&7])
|
||||
cur_key = ((54323 * cur_key) + 1) & 0xFFFFFFFF
|
||||
|
||||
return output_data
|
||||
|
||||
def encode_binary8(data, unk_key_byte):
|
||||
cur_key = ((54323 * unk_key_byte) + 1) & 0xFFFFFFFF
|
||||
|
||||
output_data = bytearray()
|
||||
for i in range(len(data)):
|
||||
output_data.append(data[i] ^ (BINARY8_KEY[i&7] ^ ((cur_key >> 13) & 0xFF)))
|
||||
cur_key = ((54323 * cur_key) + 1) & 0xFFFFFFFF
|
||||
|
||||
return output_data
|
||||
|
||||
|
||||
SUM32_TABLE_0 = bytes([0x35, 0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12])
|
||||
SUM32_TABLE_1 = bytes([0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12, 0xDE, 0xDE, 0x35])
|
||||
def calc_sum32(data):
|
||||
length = len(data)
|
||||
|
||||
t0_i = length & 0xFF
|
||||
t1_i = data[length >> 1]
|
||||
|
||||
out = bytearray(4)
|
||||
for i in range(len(data)):
|
||||
t0_i += 1
|
||||
t1_i += 1
|
||||
|
||||
tmp = (SUM32_TABLE_1[t1_i % 9] ^ SUM32_TABLE_0[t0_i % 7]) ^ data[i]
|
||||
out[i & 3] = (out[i & 3] + tmp) & 0xFF
|
||||
|
||||
return Int32ub.parse(out)
|
||||
|
||||
def read_binary8_part(stream):
|
||||
# Read the header and decrypt the header first to get the size.
|
||||
enc_bytes = bytearray(stream.read(12))
|
||||
dec_header_bytes = decode_binary8(enc_bytes[1:], enc_bytes[0])
|
||||
header = Binary8Header.parse(dec_header_bytes)
|
||||
|
||||
# Then read the body, append to the header, and decrypt the full thing.
|
||||
body_bytes = stream.read(header.body_size)
|
||||
enc_bytes.extend(body_bytes)
|
||||
dec_bytes = decode_binary8(enc_bytes[1:], enc_bytes[0])
|
||||
|
||||
# Then return the parsed header and just the raw body data.
|
||||
return (enc_bytes[0], header, dec_bytes[11:], dec_bytes)
|
||||
|
||||
def write_binary8_part(key, server_type, entry_count, payload):
|
||||
body = Binary8Header.build(dict(
|
||||
server_type=server_type,
|
||||
entry_count=entry_count,
|
||||
body_size=len(payload),
|
||||
checksum=calc_sum32(payload),
|
||||
))
|
||||
|
||||
temp = bytearray()
|
||||
temp.extend(body)
|
||||
temp.extend(payload)
|
||||
|
||||
out = bytearray()
|
||||
out.append(key)
|
||||
out.extend(encode_binary8(temp, key))
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def pad_bytes_to_len(b, length):
|
||||
out = bytearray(b)
|
||||
diff = length-len(out)
|
||||
out.extend(bytearray(diff))
|
||||
return bytes(out)
|
||||
|
||||
|
||||
|
||||
def make_custom_entrance_server_resp():
|
||||
# Get the userinfo_data
|
||||
with open('tw_server_list_resp.bin', 'rb') as f:
|
||||
(key, header, data, raw_dec_bytes) = read_binary8_part(f)
|
||||
userinfo_data = f.read()
|
||||
hexdump(userinfo_data)
|
||||
|
||||
server_count = 1
|
||||
|
||||
data = EntranceListComplete.build(dict(
|
||||
server_type = b'SV2',
|
||||
entry_count = server_count,
|
||||
body_size = 0xFFFF,
|
||||
checksum = 0xFFFFFFFF,
|
||||
servers = [dict(
|
||||
host_ip_4byte = 0x0100007F, #0x7F000001,#3377555739,
|
||||
unk_1 = 16,
|
||||
unk_2 = 0,
|
||||
channel_count = 1,
|
||||
server_type = 1,
|
||||
color = 0, # Server activity. 0 = green, 1 = orange, 2 = blue
|
||||
unk_6 = 3,
|
||||
name = pad_bytes_to_len("AErupe Server @localhost".encode('shift-jis'), 66),
|
||||
allowed_client_type_flags = 4096, # 4096(PC, PS3/PS4)?, 8192 == nothing?
|
||||
channels = [dict(
|
||||
port = 54001,
|
||||
unk_1 = 16,
|
||||
max_players = 100,
|
||||
current_players = 0,
|
||||
unk_4 = 0,
|
||||
unk_5 = 0,
|
||||
unk_6 = 0,
|
||||
unk_7 = 0,
|
||||
unk_8 = 0,
|
||||
unk_9 = 0,
|
||||
unk_10 = 319,
|
||||
unk_11 = 248,#254,
|
||||
unk_12 = 159,#255,
|
||||
unk_13 = 12345
|
||||
)],
|
||||
)]
|
||||
))
|
||||
|
||||
print(data)
|
||||
|
||||
reencoded = write_binary8_part(0, b'SV2', server_count, data[11:])
|
||||
with open('custom_entrance_server_resp.bin', 'wb') as f:
|
||||
f.write(reencoded)
|
||||
f.write(userinfo_data)
|
||||
|
||||
|
||||
with open('custom_entrance_server_resp.bin', 'rb') as f:
|
||||
(key, header, data, raw_dec_bytes) = read_binary8_part(f)
|
||||
print(EntranceListComplete.parse(raw_dec_bytes[0:]))
|
||||
|
||||
|
||||
|
||||
|
||||
make_custom_entrance_server_resp()
|
||||
|
||||
|
||||
"""
|
||||
with open('tw_server_list_resp.bin', 'rb') as f:
|
||||
(key, header, data, raw_dec_bytes) = read_binary8_part(f)
|
||||
print(EntranceListComplete.parse(raw_dec_bytes[0:]))
|
||||
"""
|
||||
|
||||
"""
|
||||
with open('tw_server_list_resp.bin', 'rb') as f:
|
||||
filedata = f.read()
|
||||
|
||||
rdr = io.BytesIO(filedata)
|
||||
|
||||
(key, header, data, raw_dec_bytes) = read_binary8_part(rdr)
|
||||
userinfo_data = rdr.read()
|
||||
|
||||
reencoded = write_binary8_part(key, header.server_type, header.entry_count, data)
|
||||
|
||||
|
||||
hexdump(reencoded[:16])
|
||||
hexdump(filedata[:16])
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
with open('dec_bin8_data_dump.bin', 'rb') as f:
|
||||
print("calc_sum32: {:X}".format(calc_sum32(f.read())))
|
||||
print("want: 74EF4928")
|
||||
"""
|
||||
Reference in New Issue
Block a user