mirror of
https://github.com/CPunch/gopenfusion.git
synced 2025-10-14 05:00:20 +00:00
moved internal/protocol -> cnet/protocol
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
||||
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
|
71
cnet/protocol/encrypt.go
Normal file
71
cnet/protocol/encrypt.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_KEY = "m@rQn~W#"
|
||||
KEY_LENGTH = 8
|
||||
)
|
||||
|
||||
func encrypt_byte_change_A(ERSize int, data []byte) int {
|
||||
var num, num2, num3 int
|
||||
|
||||
for num+ERSize <= len(data) {
|
||||
num4 := num + num3
|
||||
num5 := num + (ERSize - 1 - num3)
|
||||
|
||||
b := data[num4]
|
||||
data[num4] = data[num5]
|
||||
data[num5] = b
|
||||
num += ERSize
|
||||
num3++
|
||||
if num3 > ERSize/2 {
|
||||
num3 = 0
|
||||
}
|
||||
}
|
||||
|
||||
num2 = ERSize - (num + ERSize - len(data))
|
||||
return num + num2
|
||||
}
|
||||
|
||||
func xorData(buff, key []byte, size int) {
|
||||
for i := 0; i < size; i++ {
|
||||
buff[i] ^= key[i%KEY_LENGTH]
|
||||
}
|
||||
}
|
||||
|
||||
func EncryptData(buff, key []byte) {
|
||||
ERSize := len(buff)%(KEY_LENGTH/2+1)*2 + KEY_LENGTH
|
||||
xorData(buff, key, len(buff))
|
||||
encrypt_byte_change_A(ERSize, buff)
|
||||
}
|
||||
|
||||
func DecryptData(buff, key []byte) {
|
||||
ERSize := len(buff)%(KEY_LENGTH/2+1)*2 + KEY_LENGTH
|
||||
size := encrypt_byte_change_A(ERSize, buff)
|
||||
xorData(buff, key, size)
|
||||
}
|
||||
|
||||
func CreateNewKey(uTime, iv1, iv2 uint64) []byte {
|
||||
num := iv1 + 1
|
||||
num2 := iv2 + 1
|
||||
|
||||
dEKey := uint64(binary.LittleEndian.Uint64([]byte(DEFAULT_KEY)))
|
||||
key := dEKey * (uTime * num * num2)
|
||||
buf := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(buf, uint64(key))
|
||||
return buf
|
||||
}
|
||||
|
||||
func GenSerialKey() (int64, error) {
|
||||
tmp := [8]byte{}
|
||||
if _, err := rand.Read(tmp[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// convert to uint64 && return
|
||||
return int64(binary.LittleEndian.Uint64(tmp[:])), nil
|
||||
}
|
156
cnet/protocol/packet.go
Normal file
156
cnet/protocol/packet.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
/*
|
||||
this file handles serializing (and deserializing) structs to alignment-strict c structures generated via `tools/genstructs.py`.
|
||||
see script for details on usage!
|
||||
*/
|
||||
|
||||
type Packet struct {
|
||||
readWriter io.ReadWriter
|
||||
}
|
||||
|
||||
func NewPacket(readWriter io.ReadWriter) Packet {
|
||||
return Packet{
|
||||
readWriter: readWriter,
|
||||
}
|
||||
}
|
||||
|
||||
func (pkt Packet) encodeStructField(field reflect.StructField, value reflect.Value) error {
|
||||
// log.Printf("Encoding '%s'", field.Name)
|
||||
|
||||
switch field.Type.Kind() {
|
||||
case reflect.String: // all strings in fusionfall packets are encoded as utf16, we'll need to encode it
|
||||
sz, err := strconv.Atoi(field.Tag.Get("size"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to grab string 'size' tag")
|
||||
}
|
||||
|
||||
buf16 := utf16.Encode([]rune(value.String()))
|
||||
|
||||
// len(buf16) needs to be the same size as sz
|
||||
if len(buf16) > sz {
|
||||
// truncate
|
||||
buf16 = buf16[:sz]
|
||||
} else {
|
||||
// grow
|
||||
// TODO: probably a better way to do this?
|
||||
for len(buf16) < sz {
|
||||
buf16 = append(buf16, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// write
|
||||
if err := binary.Write(pkt.readWriter, binary.LittleEndian, buf16); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := pkt.Encode(value.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// write padding bytes
|
||||
pad, err := strconv.Atoi(field.Tag.Get("pad"))
|
||||
if err == nil {
|
||||
dummy := make([]byte, pad)
|
||||
if _, err := pkt.readWriter.Write(dummy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pkt Packet) Encode(data interface{}) error {
|
||||
rv := reflect.Indirect(reflect.ValueOf(data))
|
||||
|
||||
switch rv.Kind() {
|
||||
case reflect.Struct:
|
||||
// walk through each struct fields
|
||||
sz := rv.NumField()
|
||||
for i := 0; i < sz; i++ {
|
||||
if err := pkt.encodeStructField(rv.Type().Field(i), rv.Field(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
// we pass everything else to go's binary package
|
||||
if err := binary.Write(pkt.readWriter, binary.LittleEndian, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pkt Packet) decodeStructField(field reflect.StructField, value reflect.Value) error {
|
||||
// log.Printf("Decoding '%s'", field.Name)
|
||||
|
||||
switch field.Type.Kind() {
|
||||
case reflect.String: // all strings in fusionfall packets are encoded as utf16, we'll need to decode it
|
||||
sz, err := strconv.Atoi(field.Tag.Get("size"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to grab string 'size' tag")
|
||||
}
|
||||
|
||||
buf16 := make([]uint16, sz)
|
||||
if err := binary.Read(pkt.readWriter, binary.LittleEndian, buf16); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// find null terminator
|
||||
var realSize int
|
||||
for ; realSize < len(buf16); realSize++ {
|
||||
if buf16[realSize] == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
value.SetString(string(utf16.Decode(buf16[:realSize])))
|
||||
default:
|
||||
if err := pkt.Decode(value.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// consume padding bytes
|
||||
pad, err := strconv.Atoi(field.Tag.Get("pad"))
|
||||
if err == nil {
|
||||
dummy := make([]byte, pad)
|
||||
if _, err := pkt.readWriter.Read(dummy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pkt Packet) Decode(data interface{}) error {
|
||||
rv := reflect.Indirect(reflect.ValueOf(data))
|
||||
|
||||
switch rv.Kind() {
|
||||
case reflect.Struct:
|
||||
// walk through each struct fields
|
||||
sz := rv.NumField()
|
||||
for i := 0; i < sz; i++ {
|
||||
if err := pkt.decodeStructField(rv.Type().Field(i), rv.Field(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
if err := binary.Read(pkt.readWriter, binary.LittleEndian, data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
21
cnet/protocol/pool.go
Normal file
21
cnet/protocol/pool.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var allocator = &sync.Pool{
|
||||
New: func() any { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
// grabs a *bytes.Buffer from the pool
|
||||
func GetBuffer() *bytes.Buffer {
|
||||
return allocator.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
// returns a *bytes.Buffer to the pool
|
||||
func PutBuffer(buf *bytes.Buffer) {
|
||||
buf.Reset()
|
||||
allocator.Put(buf)
|
||||
}
|
104
cnet/protocol/protocol_test.go
Normal file
104
cnet/protocol/protocol_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package protocol_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
type TestPacketData struct {
|
||||
A int32
|
||||
B int32
|
||||
UTF16Str string `size:"32"`
|
||||
Pad int16 `pad:"2"`
|
||||
C int32
|
||||
}
|
||||
|
||||
var (
|
||||
testStruct = TestPacketData{
|
||||
A: 1,
|
||||
B: 2,
|
||||
UTF16Str: "hello world",
|
||||
C: 3,
|
||||
}
|
||||
|
||||
// this is the data we expect to get from encoding the above struct
|
||||
testData = [...]byte{
|
||||
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00,
|
||||
0x6f, 0x00, 0x20, 0x00, 0x77, 0x00, 0x6f, 0x00,
|
||||
0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// this is the data we expect to get from EncryptData(testData, []byte(protocol.DEFAULT_KEY))
|
||||
encTestData = []byte{
|
||||
0x23, 0x40, 0x72, 0x51, 0x6c, 0x7e, 0x57, 0x6c,
|
||||
0x05, 0x3b, 0x17, 0x51, 0x02, 0x7e, 0x40, 0x23,
|
||||
0x02, 0x40, 0x7e, 0x51, 0x19, 0x52, 0x38, 0x23,
|
||||
0x1f, 0x40, 0x1e, 0x0a, 0x51, 0x7e, 0x57, 0x23,
|
||||
0x6d, 0x40, 0x72, 0x6e, 0x51, 0x7e, 0x57, 0x23,
|
||||
0x23, 0x40, 0x72, 0x51, 0x6e, 0x7e, 0x57, 0x6d,
|
||||
0x6d, 0x57, 0x72, 0x51, 0x6e, 0x7e, 0x40, 0x23,
|
||||
0x6d, 0x40, 0x7e, 0x51, 0x6e, 0x72, 0x57, 0x23,
|
||||
0x6d, 0x40, 0x72, 0x6e, 0x51, 0x7e, 0x57, 0x23,
|
||||
0x6d, 0x40, 0x72, 0x6d, 0x51, 0x7e, 0x57, 0x23,
|
||||
}
|
||||
)
|
||||
|
||||
func TestPacketEncode(t *testing.T) {
|
||||
is := is.New(t)
|
||||
buf := bytes.NewBuffer(nil)
|
||||
pkt := protocol.NewPacket(buf)
|
||||
|
||||
err := pkt.Encode(testStruct)
|
||||
is.NoErr(err)
|
||||
|
||||
is.True(bytes.Equal(buf.Bytes(), testData[:])) // encoded data should match expected data
|
||||
}
|
||||
|
||||
func TestPacketDecode(t *testing.T) {
|
||||
is := is.New(t)
|
||||
buf := bytes.NewBuffer(nil)
|
||||
pkt := protocol.NewPacket(buf)
|
||||
buf.Write(testData[:])
|
||||
|
||||
var test TestPacketData
|
||||
err := pkt.Decode(&test)
|
||||
is.NoErr(err)
|
||||
is.True(test == testStruct) // decoded data should match testStruct
|
||||
}
|
||||
|
||||
func TestDataEncrypt(t *testing.T) {
|
||||
is := is.New(t)
|
||||
buf := make([]byte, len(testData))
|
||||
copy(buf, testData[:])
|
||||
|
||||
protocol.EncryptData(buf, []byte(protocol.DEFAULT_KEY))
|
||||
|
||||
is.True(bytes.Equal(buf, encTestData)) // encrypted data should match expected data
|
||||
}
|
||||
|
||||
func TestDataDecrypt(t *testing.T) {
|
||||
is := is.New(t)
|
||||
buf := make([]byte, len(encTestData))
|
||||
copy(buf, encTestData)
|
||||
|
||||
protocol.DecryptData(buf, []byte(protocol.DEFAULT_KEY))
|
||||
|
||||
is.True(bytes.Equal(buf, testData[:])) // decrypted data should match expected data
|
||||
}
|
||||
|
||||
func TestCreateNewKey(t *testing.T) {
|
||||
is := is.New(t)
|
||||
key := protocol.CreateNewKey(123456789, 0x1234567890abcdef, 0x1234567890abcdef)
|
||||
|
||||
is.True(bytes.Equal(key, []byte{0x0, 0x31, 0xb8, 0xcd, 0xd, 0xc3, 0xad, 0x67})) // key should match expected data
|
||||
}
|
5981
cnet/protocol/structs.go
Normal file
5981
cnet/protocol/structs.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||
"github.com/CPunch/gopenfusion/config"
|
||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
||||
)
|
||||
|
||||
type PacketHandler func(peer *Peer, pkt protocol.Packet) error
|
||||
|
@@ -11,7 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/CPunch/gopenfusion/cnet"
|
||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
||||
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user