mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-15 08:25:09 +01:00
Port entrance server to Go
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
package main
|
package entranceserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/Andoryuuta/Erupe/network"
|
"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))
|
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")
|
data := makeResp([]ServerInfo{
|
||||||
if err != nil {
|
ServerInfo{
|
||||||
print(err)
|
IP: net.ParseIP("127.0.0.1"),
|
||||||
return
|
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)
|
cc.SendPacket(data)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doEntranceServer(listenAddr string) {
|
func DoEntranceServer(listenAddr string) {
|
||||||
l, err := net.Listen("tcp", listenAddr)
|
l, err := net.Listen("tcp", listenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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"
|
"time"
|
||||||
|
|
||||||
"github.com/Andoryuuta/Erupe/channelserver"
|
"github.com/Andoryuuta/Erupe/channelserver"
|
||||||
_ "github.com/Andoryuuta/Erupe/network/mhfpacket"
|
|
||||||
"github.com/Andoryuuta/Erupe/signserver"
|
"github.com/Andoryuuta/Erupe/signserver"
|
||||||
|
"github.com/Andoryuuta/Erupe/entranceserver"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ func main() {
|
|||||||
|
|
||||||
// Finally start our server(s).
|
// Finally start our server(s).
|
||||||
go serveLauncherHTML(":80", false)
|
go serveLauncherHTML(":80", false)
|
||||||
go doEntranceServer(":53310")
|
go entranceserver.DoEntranceServer(":53310")
|
||||||
|
|
||||||
signServer := signserver.NewServer(
|
signServer := signserver.NewServer(
|
||||||
&signserver.Config{
|
&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