Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to gRPC based service #1

Merged
merged 16 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ jobs:
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Installing protoc-gen-go
run: |
go get google.golang.org/protobuf/cmd/protoc-gen-go
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
- name: Protogen
run: protoc protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative $(find $PROTO_DIR -type f -name '*.proto')
run: protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative $(find . -type f -name '*.proto')
- name: Build
run: go build -v ./...
- name: Test
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@
*.out

# Dependency directories (remove the comment below to include it)
github.com/
# vendor/

# Generated proto files
*.pb.go

# Decrypters
cypher_*.go
opcodes.txt

# Go workspace file
go.work

# VScode stuff
.vscode/
134 changes: 134 additions & 0 deletions client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package main

import (
"context"
"flag"
"fmt"
"io"
"log/slog"
"os"

"github.com/davecgh/go-spew/spew"
"github.com/nomoresecretz/ghoeq/common/decoder"
pb "github.com/nomoresecretz/ghoeq/common/proto/ghoeq"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)

var (
serverAddr = flag.String("server", "127.0.0.1:6420", "Server target info")
src = flag.String("source", "", "server capture source")
opFile = flag.String("opFile", "opcodes.txt", "opcode mapping data file")
)

// Simple rpc test client
func main() {
flag.Parse()
err := doStuff(context.Background())
if err != nil {
slog.Error("failed to do x: %w", err)
os.Exit(-1)
}
}

func doStuff(ctx context.Context) error {
conn, err := grpc.DialContext(ctx, *serverAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
defer conn.Close()

dec := decoder.NewDecoder()
if *opFile != "" {
if err := dec.LoadMap(*opFile); err != nil {
return err
}
}
c := pb.NewBackendServerClient(conn)

// Until auto sessions are in, we have to do the grunt work.
streamid, err := getSessionID(ctx, c)
if err != nil {
return err
}

slog.Info("identified capture session", "session", streamid)
stream, err := c.AttachStreamRaw(ctx, &pb.AttachStreamRawRequest{
Id: streamid,
Nonce: "0",
})
if err != nil {
return err
}
slog.Info("connected, beginning stream")
for {
p, err := stream.Recv()
if err == io.EOF {
slog.Info("server ended stream")
break
}
if err != nil {
return err
}
opRaw := p.GetOpCode()
op := dec.GetOp(uint16(opRaw))
if op == "" {
op = fmt.Sprintf("%#4x", opRaw)
}
// TODO: move opcode decoder to independent common module. Add api to push/pull from server.
fmt.Printf("Packet %#4X : OpCode %s %s", 0x0, op, spew.Sdump(p.GetData()))
}

return nil
}

func getSessionID(ctx context.Context, c pb.BackendServerClient) (string, error) {
s, err := c.ListSession(ctx, &pb.ListRequest{})
if err != nil {
return "", err
}
if len(s.Sessions) == 0 {
src, err := getSource(ctx, c)
if err != nil {
return "", err
}
_, err = c.ModifySession(ctx, &pb.ModifySessionRequest{
Nonce: "0",
Mods: []*pb.ModifyRequest{
{
State: pb.State_STATE_START,
Source: src,
},
},
})
if err != nil {
return "", err
}
s, err = c.ListSession(ctx, &pb.ListRequest{})
if err != nil {
return "", err
}
}
switch l := len(s.GetSessions()); {
case l == 0:
return "", fmt.Errorf("no capture session avaliable")
case l > 1:
return "", fmt.Errorf("too many active sessions to pick one. select manually")
}
return s.GetSessions()[0].GetId(), nil
}

func getSource(ctx context.Context, c pb.BackendServerClient) (string, error) {
if *src != "" {
return *src, nil
}
s, err := c.ListSources(ctx, &pb.ListRequest{})
if err != nil {
return "", err
}
sources := s.GetSources()
if len(sources) != 1 {
return "", fmt.Errorf("too many sources, pick one manually")
}
return sources[0].GetId(), nil
}
4 changes: 2 additions & 2 deletions server/opcodes.go → common/decoder/opcodes.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package decoder

import (
"bufio"
Expand All @@ -16,7 +16,7 @@ type decoder struct {

func (d *decoder) GetOp(op uint16) string {
d.mu.RLock()
s, _ := d.om[op]
s := d.om[op]
d.mu.RUnlock()
return s
}
Expand Down
106 changes: 106 additions & 0 deletions common/proto/ghoeq/ghoeq.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
syntax = "proto3";

option go_package = "github.com/nomoresecretz/ghoeq/common/proto/ghoeq";
//import "google/protobuf/DateTime.proto";

package ghoeq;

service BackendServer {
rpc ListSources (ListRequest) returns (ListSourcesResponse) {}
rpc ListSession (ListRequest) returns (ListSessionResponse) {}
rpc ListStreams (ListRequest) returns (ListStreamsResponse) {}
rpc ModifySession (ModifySessionRequest) returns (SessionResponse) {}
rpc AttachStreamRaw(AttachStreamRawRequest) returns (stream APPacket) {} // Simply a stream of decrypted / decompressed opcode + data packets
rpc AttachStreamStruct(AttachStreamStructRequest) returns (stream ClientPacket) {} // Normalized proper data packets - long term plan once backend can properly deseralize
}

message ListRequest {
string nonce = 1;
string id = 2;
}

message ListStreamsResponse {
repeated Stream streams = 1;
}

message SessionResponse {}

message AttachStreamRawRequest {
string id = 1; // Start pulling a specific stream, or 0 for first avaliable if one or none.
string nonce = 2;
uint64 seq = 3; // Used for resume from previous sequence number. Not implmented yet.
}

message ListSourcesResponse {
repeated Source sources = 1;
}

message ListSessionResponse {
repeated Session sessions = 1;
}

message ModifySessionRequest {
repeated ModifyRequest mods = 1;
string nonce = 2;
}

message ModifyRequest {
uint32 id = 1;
State state = 2;
string source = 3;
}

message Session {
string id = 1;
string source = 2; //Interface / PacketCapture etc
}

message Source {
string id = 1;
string description = 2;
}

message Stream {
uint32 id = 1;
string source = 2; //Session / source
//google.protobuf.DateTime first_seen = 3;
Client client = 4;
repeated StreamThread sessions = 5;
}

message Client {
string address = 1;
}

message StreamThread {
string id = 1;
uint32 port = 2;
string peer_address = 3;
uint32 peer_port = 4;
PeerType type = 5;
}

enum State {
STATE_UNKNOWN = 0;
STATE_START = 1;
STATE_STOP = 2;
}

message APPacket {
uint64 seq = 1;
uint32 op_code = 2;
bytes data = 3;
string thread_ID = 4;
}

message ClientPacket {}

message AttachStreamStructRequest {}

enum PeerType {
PEER_UNKNOWN = 0;
PEER_LOGIN = 1;
PEER_WORLD = 2;
PEER_ZONE = 3;
PEER_CHAT = 4;
}
99 changes: 99 additions & 0 deletions common/struct/PlayerProfile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package eqstruct

type PlayerProfile struct {
Checksum uint32
Name, LastName string
UniqueGuildID uint32
Gender uint8
GenderChar string
Race uint16
Class uint16
BodyType uint16
Level uint8
LevelChar string
Exp uint32
Points int16
Mana int16
CurHP int16
Status uint16
STR, STA, CHA, DEX, INT, AGI, WIS int16
Face uint8
EquipType []uint8
TintProfile []byte
Inventory []uint16
Languages []uint8
// TODO: ItemPropertiesStruct

Check failure on line 25 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

common/struct/PlayerProfile.go:25: Line contains TODO/BUG/FIXME: "TODO: ItemPropertiesStruct" (godox)
// TODO: SpellBuffStruct

Check failure on line 26 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

common/struct/PlayerProfile.go:26: Line contains TODO/BUG/FIXME: "TODO: SpellBuffStruct" (godox)
ContainerInv []int16
CursorBagInv []int16
// TODO: ItemPropertiesStruct (Container)

Check failure on line 29 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

common/struct/PlayerProfile.go:29: Line contains TODO/BUG/FIXME: "TODO: ItemPropertiesStruct (Container)" (godox)
// TODO: ItemPropertiesStruct (Cursor Container)

Check failure on line 30 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

common/struct/PlayerProfile.go:30: Line contains TODO/BUG/FIXME: "TODO: ItemPropertiesStruct (Cursor Conta..." (godox)
SpellBook []int16
Unknown2374 byte
MemSpells []int16
AvaliableSlots uint16
Y, X, Z, Heading float32
Position uint32
Platinum, Gold, Silver, Copper int32
PlatinumBank, GoldBank, SilverBank, CopperBank int32
PlatinumCursor, GoldCursor, SilverCursor, CopperCursor int32
Currency []int32
Skills []int16
InnateSkills []int16
AirSupply, Texture uint8
Height, Width, Length, ViewHeight float32
Boat string
Autosplit uint8
Expansions uint8
Hunger, Thirst int32
ZoneID uint32
BindPoints []byte // TODO: BindPointStruct

Check failure on line 50 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

common/struct/PlayerProfile.go:50: Line contains TODO/BUG/FIXME: "TODO: BindPointStruct" (godox)
// TODO: Bank ItemStructs

Check failure on line 51 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

common/struct/PlayerProfile.go:51: Line contains TODO/BUG/FIXME: "TODO: Bank ItemStructs" (godox)
// TODO: Bank Bag ItemStructs

Check failure on line 52 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

common/struct/PlayerProfile.go:52: Line contains TODO/BUG/FIXME: "TODO: Bank Bag ItemStructs" (godox)
LoginTime uint32

Check failure on line 53 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

line is 171 characters (lll)
BankInv []int16

Check failure on line 54 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

line is 172 characters (lll)
BankInvCont []int16
Deity uint16
GuildID uint16
Birthday uint32
LastLogin uint32
TimePlayedMin uint32
Fatigue int8
PVP uint8
Level2 uint8
ANON uint8
GM uint8
GuildRank uint8
Intoxication uint8
EQBackground uint8
SpellSlotRefresh []uint32
AbilitySlotRefresh []uint32
GroupMembers []string
GroupDat uint32
EXPAA uint32
Title uint8
PerAA uint8
Haircolor, Beardcolor, Eye1color, Eye2color, Hairstyle, Beard, Luclinface uint8
ItemMaterial []byte
AAArray []byte
ATR_DivineRez, ATR_FreeHot, ATR_TargetDA, ATR_SPTWood, ATR_DireCharm, ATR_StrongRoot, ATR_Masco, ATR_MANABURN, ATR_GatherMana, ATR_PetLOH, ATR_Exodus, ATR_MassFear uint32
AirRemaining uint16
AAPts uint16
MGBTimer uint32
MBitFlags []int8
PopSpellTimer uint32
LastSheild, LastModulated uint32
bPointer int
}

func (p *PlayerProfile) EQType() EQType { return EQT_PlayerProfile }
func (p *PlayerProfile) bp() *int { return &p.bPointer}

Check failure on line 90 in common/struct/PlayerProfile.go

View workflow job for this annotation

GitHub Actions / golangci

File is not `gofmt`-ed with `-s` (gofmt)

func (p *PlayerProfile) Deserialize(b []byte) (*PlayerProfile, error) {
p.bPointer = 0

if err := EQRead(b, p, &p.Gender, 0); err != nil {
return nil, err
}
return p, nil
}
Loading
Loading