mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-16 17:05:03 +01:00
Now gets ingame!
This commit is contained in:
@@ -3,28 +3,238 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/Andoryuuta/Erupe/network"
|
"github.com/Andoryuuta/Erupe/network"
|
||||||
"github.com/Andoryuuta/byteframe"
|
"github.com/Andoryuuta/byteframe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var loadDataCount int
|
||||||
|
var getPaperDataCount int
|
||||||
|
|
||||||
|
func handlePacket(cc *network.CryptConn, pkt []byte) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println("Recovered from panic.")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(pkt)
|
||||||
|
opcode := network.PacketID(bf.ReadUint16())
|
||||||
|
|
||||||
|
if opcode == network.MSG_SYS_EXTEND_THRESHOLD {
|
||||||
|
opcode = network.PacketID(bf.ReadUint16())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch opcode {
|
||||||
|
case network.MSG_SYS_PING:
|
||||||
|
ackHandle := bf.ReadUint32()
|
||||||
|
_ = bf.ReadUint16()
|
||||||
|
|
||||||
|
bfw := byteframe.NewByteFrame()
|
||||||
|
bfw.WriteUint16(uint16(network.MSG_SYS_ACK))
|
||||||
|
bfw.WriteUint32(ackHandle)
|
||||||
|
bfw.WriteUint32(0)
|
||||||
|
bfw.WriteUint32(0)
|
||||||
|
cc.SendPacket(bfw.Data())
|
||||||
|
case network.MSG_SYS_TIME:
|
||||||
|
_ = bf.ReadUint8()
|
||||||
|
timestamp := bf.ReadUint32() // unix timestamp, e.g. 1577105879
|
||||||
|
|
||||||
|
bfw := byteframe.NewByteFrame()
|
||||||
|
bfw.WriteUint16(uint16(network.MSG_SYS_TIME))
|
||||||
|
bfw.WriteUint8(0)
|
||||||
|
bfw.WriteUint32(timestamp)
|
||||||
|
cc.SendPacket(bfw.Data())
|
||||||
|
case network.MSG_SYS_LOGIN:
|
||||||
|
ackHandle := bf.ReadUint32()
|
||||||
|
charID0 := bf.ReadUint32()
|
||||||
|
loginTokenNumber := bf.ReadUint32()
|
||||||
|
hardcodedZero0 := bf.ReadUint16()
|
||||||
|
requestVersion := bf.ReadUint16()
|
||||||
|
charID1 := bf.ReadUint32()
|
||||||
|
hardcodedZero1 := bf.ReadUint16()
|
||||||
|
loginTokenLength := bf.ReadUint16() // hardcoded to 0x11
|
||||||
|
loginTokenString := bf.ReadBytes(17)
|
||||||
|
|
||||||
|
_ = ackHandle
|
||||||
|
_ = charID0
|
||||||
|
_ = loginTokenNumber
|
||||||
|
_ = hardcodedZero0
|
||||||
|
_ = requestVersion
|
||||||
|
_ = charID1
|
||||||
|
_ = hardcodedZero1
|
||||||
|
_ = loginTokenLength
|
||||||
|
_ = loginTokenString
|
||||||
|
|
||||||
|
bfw := byteframe.NewByteFrame()
|
||||||
|
bfw.WriteUint16(uint16(network.MSG_SYS_ACK))
|
||||||
|
bfw.WriteUint32(ackHandle)
|
||||||
|
bfw.WriteUint64(0x000000005E00B9C2) // Timestamp?
|
||||||
|
cc.SendPacket(bfw.Data())
|
||||||
|
|
||||||
|
case network.MSG_MHF_ENUMERATE_EVENT:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_ENUMERATE_QUEST:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_ENUMERATE_RANKING:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_READ_MERCENARY_W:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_ETC_POINTS:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_READ_GUILDCARD:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_READ_BEAT_LEVEL:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_EARTH_STATUS:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_EARTH_VALUE:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_WEEKLY_SCHEDULE:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LIST_MEMBER:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_PLATE_DATA:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_PLATE_BOX:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_FAVORITE_QUEST:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_PARTNER:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_TOWER_INFO:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_OTOMO_AIROU:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_DECO_MYSET:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_HUNTER_NAVI:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_UD_SCHEDULE:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_UD_INFO:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_UD_MONSTER_POINT:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_RAND_FROM_TABLE:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_ACQUIRE_MONTHLY_REWARD:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_RENGOKU_RANKING_RANK:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_PLATE_MYSET:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_RENGOKU_DATA:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_ENUMERATE_SHOP:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_LOAD_SCENARIO_DATA:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_BOOST_TIME_LIMIT:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_BOOST_RIGHT:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_REWARD_SONG:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_GACHA_POINT:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_KOURYOU_POINT:
|
||||||
|
fallthrough
|
||||||
|
case network.MSG_MHF_GET_ENHANCED_MINIDATA:
|
||||||
|
|
||||||
|
ackHandle := bf.ReadUint32()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp.bin", opcode.String()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bfw := byteframe.NewByteFrame()
|
||||||
|
bfw.WriteUint16(uint16(network.MSG_SYS_ACK))
|
||||||
|
bfw.WriteUint32(ackHandle)
|
||||||
|
bfw.WriteBytes(data)
|
||||||
|
cc.SendPacket(bfw.Data())
|
||||||
|
|
||||||
|
case network.MSG_MHF_INFO_FESTA:
|
||||||
|
ackHandle := bf.ReadUint32()
|
||||||
|
_ = bf.ReadUint32()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp.bin", opcode.String()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bfw := byteframe.NewByteFrame()
|
||||||
|
bfw.WriteUint16(uint16(network.MSG_SYS_ACK))
|
||||||
|
bfw.WriteUint32(ackHandle)
|
||||||
|
bfw.WriteBytes(data)
|
||||||
|
cc.SendPacket(bfw.Data())
|
||||||
|
|
||||||
|
case network.MSG_MHF_LOADDATA:
|
||||||
|
ackHandle := bf.ReadUint32()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp%d.bin", opcode.String(), loadDataCount))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bfw := byteframe.NewByteFrame()
|
||||||
|
bfw.WriteUint16(uint16(network.MSG_SYS_ACK))
|
||||||
|
bfw.WriteUint32(ackHandle)
|
||||||
|
bfw.WriteBytes(data)
|
||||||
|
cc.SendPacket(bfw.Data())
|
||||||
|
|
||||||
|
loadDataCount++
|
||||||
|
if loadDataCount > 1 {
|
||||||
|
loadDataCount = 0
|
||||||
|
}
|
||||||
|
case network.MSG_MHF_GET_PAPER_DATA:
|
||||||
|
ackHandle := bf.ReadUint32()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp%d.bin", opcode.String(), getPaperDataCount))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bfw := byteframe.NewByteFrame()
|
||||||
|
bfw.WriteUint16(uint16(network.MSG_SYS_ACK))
|
||||||
|
bfw.WriteUint32(ackHandle)
|
||||||
|
bfw.WriteBytes(data)
|
||||||
|
cc.SendPacket(bfw.Data())
|
||||||
|
|
||||||
|
getPaperDataCount++
|
||||||
|
if getPaperDataCount > 7 {
|
||||||
|
getPaperDataCount = 0
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Opcode: %s\n", opcode)
|
||||||
|
fmt.Printf("Data:\n%s\n", hex.Dump(pkt))
|
||||||
|
|
||||||
|
remainingData := bf.DataFromCurrent()
|
||||||
|
if len(remainingData) >= 2 && (opcode == network.MSG_SYS_TIME || opcode == network.MSG_MHF_INFO_FESTA) {
|
||||||
|
handlePacket(cc, remainingData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handleChannelServerConnection(conn net.Conn) {
|
func handleChannelServerConnection(conn net.Conn) {
|
||||||
fmt.Println("Channel server got connection!")
|
fmt.Println("Channel server got connection!")
|
||||||
// Unlike the sign and entrance server,
|
// Unlike the sign and entrance server,
|
||||||
// the client DOES NOT initalize the channel connection with 8 NULL bytes.
|
// the client DOES NOT initalize the channel connection with 8 NULL bytes.
|
||||||
|
|
||||||
cc := network.NewCryptConn(conn)
|
cc := network.NewCryptConn(conn)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
pkt, err := cc.ReadPacket()
|
pkt, err := cc.ReadPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bf := byteframe.NewByteFrameFromBytes(pkt)
|
handlePacket(cc, pkt)
|
||||||
opcode := network.PacketID(bf.ReadUint16())
|
|
||||||
fmt.Printf("Opcode: %s\n", opcode)
|
|
||||||
fmt.Printf("Data:\n%s\n", hex.Dump(pkt))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -7,5 +7,7 @@ require (
|
|||||||
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0
|
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0
|
||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/golang-migrate/migrate v3.5.4+incompatible // indirect
|
github.com/golang-migrate/migrate v3.5.4+incompatible // indirect
|
||||||
|
github.com/gorilla/handlers v1.4.2
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
|
github.com/lib/pq v1.3.0
|
||||||
)
|
)
|
||||||
|
|||||||
5
go.sum
5
go.sum
@@ -2,9 +2,14 @@ github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3 h1:N8pCiqpJAHyOO8
|
|||||||
github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3/go.mod h1:4WK1jUpH8NFdDiv7IJcBfyCIOMqKjZ15kcw5eBKALvc=
|
github.com/Andoryuuta/binio v0.0.0-20160731013325-2c89946fb8c3/go.mod h1:4WK1jUpH8NFdDiv7IJcBfyCIOMqKjZ15kcw5eBKALvc=
|
||||||
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0 h1:2pVgen9rh18IxSWxOa80bObcpyfrS6d5bJtZeCUN7rY=
|
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0 h1:2pVgen9rh18IxSWxOa80bObcpyfrS6d5bJtZeCUN7rY=
|
||||||
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0/go.mod h1:koVyx+gN3TfE70rpOidywETVODk87304YpwW69Y27J4=
|
github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0/go.mod h1:koVyx+gN3TfE70rpOidywETVODk87304YpwW69Y27J4=
|
||||||
|
github.com/Andoryuuta/erupe v0.0.0-20191219210047-7aef17f7d946 h1:Z20gk8dvCNRZuHEdeEyGVwbMs9IxyGs5gGU4zFN3aTs=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
|
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
|
||||||
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
|
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
|
||||||
|
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
|
||||||
|
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||||
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gorilla/handlers"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,15 +15,27 @@ func g6Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serverUniqueName(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
|
dump, err := httputil.DumpRequest(r, true)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(string(dump))
|
||||||
|
|
||||||
|
fmt.Fprintf(w, `<?xml version="1.0" encoding="ISO-8859-1"?><uniq code="200">OK</uniq>`)
|
||||||
|
}
|
||||||
|
|
||||||
// serveLauncherHTML is responsible for serving the launcher HTML and (HACK) serverlist.xml.
|
// serveLauncherHTML is responsible for serving the launcher HTML and (HACK) serverlist.xml.
|
||||||
func serveLauncherHTML(listenAddr string) {
|
func serveLauncherHTML(listenAddr string) {
|
||||||
// Manually route the folder root to index.html? Is there a better way to do this?
|
// Manually route the folder root to index.html? Is there a better way to do this?
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
router.GET("/g6_launcher/", g6Index)
|
router.GET("/g6_launcher/", g6Index)
|
||||||
|
router.GET("/server/unique.php", serverUniqueName)
|
||||||
|
|
||||||
static := httprouter.New()
|
static := httprouter.New()
|
||||||
static.ServeFiles("/*filepath", http.Dir("www"))
|
static.ServeFiles("/*filepath", http.Dir("www"))
|
||||||
router.NotFound = static
|
router.NotFound = static
|
||||||
|
|
||||||
http.ListenAndServe(listenAddr, router)
|
http.ListenAndServe(listenAddr, handlers.LoggingHandler(os.Stdout, router))
|
||||||
}
|
}
|
||||||
|
|||||||
29
main.go
29
main.go
@@ -1,16 +1,43 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Andoryuuta/Erupe/signserver"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Starting!")
|
fmt.Println("Starting!")
|
||||||
|
|
||||||
|
// Load the config.toml configuration.
|
||||||
|
// TODO(Andoryuuta): implement config loading.
|
||||||
|
|
||||||
|
// Create the postgres DB pool.
|
||||||
|
db, err := sql.Open("postgres", "host=localhost port=5432 user=postgres password=admin dbname=erupe sslmode=disable")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the DB connection.
|
||||||
|
err = db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally start our server(s).
|
||||||
go serveLauncherHTML(":80")
|
go serveLauncherHTML(":80")
|
||||||
go doEntranceServer(":53310")
|
go doEntranceServer(":53310")
|
||||||
go doSignServer(":53312")
|
|
||||||
|
signServer := signserver.NewServer(
|
||||||
|
&signserver.Config{
|
||||||
|
DB: db,
|
||||||
|
ListenAddr: ":53312",
|
||||||
|
})
|
||||||
|
go signServer.Listen()
|
||||||
|
|
||||||
go doChannelServer(":54001")
|
go doChannelServer(":54001")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -1,27 +1,6 @@
|
|||||||
package main
|
package signserver
|
||||||
|
|
||||||
import (
|
import "github.com/Andoryuuta/byteframe"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/Andoryuuta/Erupe/network"
|
|
||||||
"github.com/Andoryuuta/byteframe"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
var conf *config.Config
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
c, err := config.LoadConfig("config.toml")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conf = c
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func paddedString(x string, size uint) []byte {
|
func paddedString(x string, size uint) []byte {
|
||||||
out := make([]byte, size)
|
out := make([]byte, size)
|
||||||
@@ -42,6 +21,12 @@ func uint16PascalString(bf *byteframe.ByteFrame, x string) {
|
|||||||
bf.WriteNullTerminatedBytes([]byte(x))
|
bf.WriteNullTerminatedBytes([]byte(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeSignInFailureResp(respID RespID) []byte {
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint8(uint8(respID))
|
||||||
|
return bf.Data()
|
||||||
|
}
|
||||||
|
|
||||||
func makeSignInResp(username string) []byte {
|
func makeSignInResp(username string) []byte {
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
@@ -51,7 +36,7 @@ func makeSignInResp(username string) []byte {
|
|||||||
|
|
||||||
bf.WriteUint8(1) // resp_code
|
bf.WriteUint8(1) // resp_code
|
||||||
bf.WriteUint8(0) // file/patch server count
|
bf.WriteUint8(0) // file/patch server count
|
||||||
bf.WriteUint8(1) // entrance server count
|
bf.WriteUint8(4) // entrance server count
|
||||||
bf.WriteUint8(1) // character count
|
bf.WriteUint8(1) // character count
|
||||||
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
||||||
bf.WriteBytes(paddedString("logintokenstrng", 16)) // login_token (16 byte padded string)
|
bf.WriteBytes(paddedString("logintokenstrng", 16)) // login_token (16 byte padded string)
|
||||||
@@ -61,6 +46,9 @@ func makeSignInResp(username string) []byte {
|
|||||||
|
|
||||||
// Array(this.entrance_server_count, PascalString(Byte, "utf8")),
|
// Array(this.entrance_server_count, PascalString(Byte, "utf8")),
|
||||||
uint8PascalString(bf, "localhost:53310")
|
uint8PascalString(bf, "localhost:53310")
|
||||||
|
uint8PascalString(bf, "")
|
||||||
|
uint8PascalString(bf, "")
|
||||||
|
uint8PascalString(bf, "mhf-n.capcom.com.tw")
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
// Characters:
|
// Characters:
|
||||||
@@ -87,6 +75,8 @@ func makeSignInResp(username string) []byte {
|
|||||||
bf.WriteUint32(469153291) // character ID 469153291
|
bf.WriteUint32(469153291) // character ID 469153291
|
||||||
bf.WriteUint16(30) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
bf.WriteUint16(30) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
||||||
|
|
||||||
|
//44.204
|
||||||
|
|
||||||
/*
|
/*
|
||||||
0=大劍/Big sword
|
0=大劍/Big sword
|
||||||
1=重弩/Heavy crossbow
|
1=重弩/Heavy crossbow
|
||||||
@@ -108,7 +98,7 @@ func makeSignInResp(username string) []byte {
|
|||||||
|
|
||||||
bf.WriteUint32(1576761172) // Last login date, unix timestamp in seconds.
|
bf.WriteUint32(1576761172) // Last login date, unix timestamp in seconds.
|
||||||
bf.WriteUint8(1) // Sex, 0=male, 1=female.
|
bf.WriteUint8(1) // Sex, 0=male, 1=female.
|
||||||
bf.WriteUint8(0) // Is new character, 1 replaces character name with ?????.
|
bf.WriteUint8(1) // Is new character, 1 replaces character name with ?????.
|
||||||
grMode := uint8(0)
|
grMode := uint8(0)
|
||||||
bf.WriteUint8(1) // GR level if grMode == 0
|
bf.WriteUint8(1) // GR level if grMode == 0
|
||||||
bf.WriteUint8(grMode) // GR mode.
|
bf.WriteUint8(grMode) // GR mode.
|
||||||
@@ -145,51 +135,3 @@ func makeSignInResp(username string) []byte {
|
|||||||
|
|
||||||
return bf.Data()
|
return bf.Data()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSignServerConnection(conn net.Conn) {
|
|
||||||
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
|
||||||
nullInit := make([]byte, 8)
|
|
||||||
n, err := io.ReadFull(conn, nullInit)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
} else if n != len(nullInit) {
|
|
||||||
fmt.Println("io.ReadFull couldn't read the full 8 byte init.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cc := network.NewCryptConn(conn)
|
|
||||||
for {
|
|
||||||
pkt, err := cc.ReadPacket()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bf := byteframe.NewByteFrameFromBytes(pkt)
|
|
||||||
loginType := string(bf.ReadNullTerminatedBytes())
|
|
||||||
username := string(bf.ReadNullTerminatedBytes())
|
|
||||||
password := string(bf.ReadNullTerminatedBytes())
|
|
||||||
unk := string(bf.ReadNullTerminatedBytes())
|
|
||||||
fmt.Printf("Got signin, type: %s, username: %s, password %s, unk: %s", loginType, username, password, unk)
|
|
||||||
|
|
||||||
resp := makeSignInResp(username)
|
|
||||||
cc.SendPacket(resp)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func doSignServer(listenAddr string) {
|
|
||||||
l, err := net.Listen("tcp", listenAddr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
go handleSignServerConnection(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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