mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-16 08:55:31 +01:00
Now gets ingame!
This commit is contained in:
137
signserver/dsgn_resp.go
Normal file
137
signserver/dsgn_resp.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package signserver
|
||||
|
||||
import "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
|
||||
}
|
||||
|
||||
func uint8PascalString(bf *byteframe.ByteFrame, x string) {
|
||||
bf.WriteUint8(uint8(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func uint16PascalString(bf *byteframe.ByteFrame, x string) {
|
||||
bf.WriteUint16(uint16(len(x) + 1))
|
||||
bf.WriteNullTerminatedBytes([]byte(x))
|
||||
}
|
||||
|
||||
func makeSignInFailureResp(respID RespID) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(uint8(respID))
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func makeSignInResp(username string) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
// delete me:
|
||||
//bf.WriteUint8(8)
|
||||
//return bf.Data()
|
||||
|
||||
bf.WriteUint8(1) // resp_code
|
||||
bf.WriteUint8(0) // file/patch server count
|
||||
bf.WriteUint8(4) // entrance server count
|
||||
bf.WriteUint8(1) // character count
|
||||
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
||||
bf.WriteBytes(paddedString("logintokenstrng", 16)) // login_token (16 byte padded string)
|
||||
bf.WriteUint32(1576761190)
|
||||
|
||||
// file patch server PascalStrings here
|
||||
|
||||
// Array(this.entrance_server_count, PascalString(Byte, "utf8")),
|
||||
uint8PascalString(bf, "localhost:53310")
|
||||
uint8PascalString(bf, "")
|
||||
uint8PascalString(bf, "")
|
||||
uint8PascalString(bf, "mhf-n.capcom.com.tw")
|
||||
|
||||
///////////////////////////
|
||||
// Characters:
|
||||
|
||||
/*
|
||||
tab = '123456789ABCDEFGHJKLMNPQRTUVWXYZ'
|
||||
def make_uid_str(cid):
|
||||
out = ''
|
||||
for i in range(6):
|
||||
v = (cid>>5*i)
|
||||
out += tab[v&0x1f]
|
||||
return out
|
||||
|
||||
def make_cid_int(uid):
|
||||
v = 0
|
||||
for c in uid[::-1]:
|
||||
idx = tab.find(c)
|
||||
if idx == -1:
|
||||
raise Exception("not in tab")
|
||||
v |= idx
|
||||
v = v<<5
|
||||
return v>>5
|
||||
*/
|
||||
bf.WriteUint32(469153291) // character ID 469153291
|
||||
bf.WriteUint16(30) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
||||
|
||||
//44.204
|
||||
|
||||
/*
|
||||
0=大劍/Big sword
|
||||
1=重弩/Heavy crossbow
|
||||
2=大錘/Sledgehammer
|
||||
3=長槍/Spear
|
||||
4=單手劍/One-handed sword
|
||||
5=輕弩/Light crossbow
|
||||
6=雙劍/Double sword
|
||||
7=太刀/Tadao
|
||||
8=狩獵笛/Hunting flute
|
||||
9=銃槍/Shotgun
|
||||
10=弓/bow
|
||||
11=穿龍棍/Wear a dragon stick
|
||||
12=斬擊斧F/Chopping Axe F
|
||||
13=---
|
||||
default=不明/unknown
|
||||
*/
|
||||
bf.WriteUint16(7) // Weapon, 0-13.
|
||||
|
||||
bf.WriteUint32(1576761172) // Last login date, unix timestamp in seconds.
|
||||
bf.WriteUint8(1) // Sex, 0=male, 1=female.
|
||||
bf.WriteUint8(1) // Is new character, 1 replaces character name with ?????.
|
||||
grMode := uint8(0)
|
||||
bf.WriteUint8(1) // GR level if grMode == 0
|
||||
bf.WriteUint8(grMode) // GR mode.
|
||||
bf.WriteBytes(paddedString(username, 16)) // Character name
|
||||
bf.WriteBytes(paddedString("0", 32)) // unk str
|
||||
if grMode == 1 {
|
||||
bf.WriteUint16(55) // GR level override.
|
||||
bf.WriteUint8(0) // unk
|
||||
bf.WriteUint8(0) // unk
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
bf.WriteUint8(0) // friends_list_count
|
||||
bf.WriteUint8(0) // guild_members_count
|
||||
bf.WriteUint8(0) // notice_count
|
||||
bf.WriteUint32(0xDEADBEEF) // some_last_played_character_id
|
||||
bf.WriteUint32(14) // unk_flags
|
||||
uint8PascalString(bf, "") // unk_data_blob PascalString
|
||||
|
||||
bf.WriteUint16(51728)
|
||||
bf.WriteUint16(20000)
|
||||
uint16PascalString(bf, "1000672925")
|
||||
|
||||
bf.WriteUint8(0)
|
||||
|
||||
bf.WriteUint16(51729)
|
||||
bf.WriteUint16(1)
|
||||
bf.WriteUint16(20000)
|
||||
uint16PascalString(bf, "203.191.249.36:8080")
|
||||
|
||||
bf.WriteUint32(1578905116)
|
||||
bf.WriteUint32(0)
|
||||
|
||||
return bf.Data()
|
||||
}
|
||||
50
signserver/respid.go
Normal file
50
signserver/respid.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package signserver
|
||||
|
||||
//revive:disable
|
||||
type RespID uint16
|
||||
|
||||
//go:generate stringer -type=RespID
|
||||
const (
|
||||
SIGN_UNKNOWN RespID = iota
|
||||
SIGN_SUCCESS
|
||||
SIGN_EFAILED // Authentication server communication failed
|
||||
SIGN_EILLEGAL // Incorrect input, authentication has been suspended
|
||||
SIGN_EALERT // Authentication server process error
|
||||
SIGN_EABORT // The internal procedure of the authentication server ended abnormally
|
||||
SIGN_ERESPONSE // Procedure terminated due to abnormal certification report
|
||||
SIGN_EDATABASE // Database connection failed
|
||||
SIGN_EABSENCE
|
||||
SIGN_ERESIGN
|
||||
SIGN_ESUSPEND_D
|
||||
SIGN_ELOCK
|
||||
SIGN_EPASS
|
||||
SIGN_ERIGHT
|
||||
SIGN_EAUTH
|
||||
SIGN_ESUSPEND // This account is temporarily suspended. Please contact customer service for details
|
||||
SIGN_EELIMINATE // This account is permanently suspended. Please contact customer service for details
|
||||
SIGN_ECLOSE
|
||||
SIGN_ECLOSE_EX // Login process is congested. <br> Please try to sign in again later
|
||||
SIGN_EINTERVAL
|
||||
SIGN_EMOVED
|
||||
SIGN_ENOTREADY
|
||||
SIGN_EALREADY
|
||||
SIGN_EIPADDR // Region block because of IP address.
|
||||
SIGN_EHANGAME
|
||||
SIGN_UPD_ONLY
|
||||
SIGN_EMBID
|
||||
SIGN_ECOGCODE
|
||||
SIGN_ETOKEN
|
||||
SIGN_ECOGLINK
|
||||
SIGN_EMAINTE
|
||||
SIGN_EMAINTE_NOUPDATE
|
||||
|
||||
// Couldn't find names for the following:
|
||||
UNK_32
|
||||
UNK_33
|
||||
UNK_34
|
||||
UNK_35
|
||||
|
||||
SIGN_XBRESPONSE
|
||||
SIGN_EPSI
|
||||
SIGN_EMBID_PSI
|
||||
)
|
||||
107
signserver/session.go
Normal file
107
signserver/session.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package signserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/Andoryuuta/Erupe/network"
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
)
|
||||
|
||||
// Session holds state for the sign server connection.
|
||||
type Session struct {
|
||||
sync.Mutex
|
||||
sid int
|
||||
server *Server
|
||||
rawConn *net.Conn
|
||||
cryptConn *network.CryptConn
|
||||
}
|
||||
|
||||
func (session *Session) fail() {
|
||||
session.server.Lock()
|
||||
delete(session.server.sessions, session.sid)
|
||||
session.server.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (session *Session) work() {
|
||||
for {
|
||||
pkt, err := session.cryptConn.ReadPacket()
|
||||
if err != nil {
|
||||
session.fail()
|
||||
return
|
||||
}
|
||||
|
||||
err = session.handlePacket(pkt)
|
||||
if err != nil {
|
||||
session.fail()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (session *Session) handlePacket(pkt []byte) error {
|
||||
bf := byteframe.NewByteFrameFromBytes(pkt)
|
||||
reqType := string(bf.ReadNullTerminatedBytes())
|
||||
switch reqType {
|
||||
case "DSGN:100":
|
||||
session.handleDSGNRequest(bf)
|
||||
break
|
||||
case "DELETE:100":
|
||||
loginTokenString := string(bf.ReadNullTerminatedBytes())
|
||||
_ = loginTokenString
|
||||
characterID := bf.ReadUint32()
|
||||
|
||||
fmt.Printf("Got delete request for character ID: %v\n", characterID)
|
||||
fmt.Printf("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent()))
|
||||
default:
|
||||
fmt.Printf("Got unknown request type %s, data:\n%s\n", reqType, hex.Dump(bf.DataFromCurrent()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (session *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
|
||||
reqUsername := string(bf.ReadNullTerminatedBytes())
|
||||
reqPassword := string(bf.ReadNullTerminatedBytes())
|
||||
reqUnk := string(bf.ReadNullTerminatedBytes())
|
||||
fmt.Printf("Got sign in request:\n\tUsername: %s\n\tPassword %s\n\tUnk: %s\n", reqUsername, reqPassword, reqUnk)
|
||||
|
||||
// TODO(Andoryuuta): remove plaintext password storage if this ever becomes more than a toy project.
|
||||
var (
|
||||
id int
|
||||
password string
|
||||
)
|
||||
err := session.server.db.QueryRow("SELECT id, password FROM users WHERE username = $1", reqUsername).Scan(&id, &password)
|
||||
var serverRespBytes []byte
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
fmt.Printf("No rows for username %s\n", reqUsername)
|
||||
serverRespBytes = makeSignInFailureResp(SIGN_EAUTH)
|
||||
break
|
||||
case err != nil:
|
||||
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
|
||||
fmt.Println("Got error on SQL query!")
|
||||
fmt.Println(err)
|
||||
break
|
||||
default:
|
||||
if reqPassword == password {
|
||||
fmt.Println("Passwords match!")
|
||||
serverRespBytes = makeSignInResp(reqUsername)
|
||||
} else {
|
||||
fmt.Println("Passwords don't match!")
|
||||
serverRespBytes = makeSignInFailureResp(SIGN_EPASS)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err = session.cryptConn.SendPacket(serverRespBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
79
signserver/sign_server.go
Normal file
79
signserver/sign_server.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package signserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/Andoryuuta/Erupe/network"
|
||||
)
|
||||
|
||||
// Config struct allows configuring the server.
|
||||
type Config struct {
|
||||
DB *sql.DB
|
||||
ListenAddr string
|
||||
}
|
||||
|
||||
// Server is a MHF sign server.
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
sid int
|
||||
sessions map[int]*Session
|
||||
db *sql.DB
|
||||
listenAddr string
|
||||
}
|
||||
|
||||
// NewServer creates a new Server type.
|
||||
func NewServer(config *Config) *Server {
|
||||
s := &Server{
|
||||
sid: 0,
|
||||
sessions: make(map[int]*Session),
|
||||
db: config.DB,
|
||||
listenAddr: config.ListenAddr,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Listen listens for new connections and accepts/serves them.
|
||||
func (s *Server) Listen() {
|
||||
l, err := net.Listen("tcp", s.listenAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go s.handleConnection(s.sid, conn)
|
||||
s.sid++
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleConnection(sid int, conn net.Conn) {
|
||||
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
||||
nullInit := make([]byte, 8)
|
||||
_, err := io.ReadFull(conn, nullInit)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
session := &Session{
|
||||
server: s,
|
||||
rawConn: &conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
s.sessions[sid] = session
|
||||
s.Unlock()
|
||||
|
||||
session.work()
|
||||
}
|
||||
Reference in New Issue
Block a user