Compare commits

..

8 Commits

Author SHA1 Message Date
626c98ba94 Implement Frames 2026-05-20 11:17:17 +02:00
64f902ba9d Use fake server for engine connect and disconnect tests 2026-05-20 11:16:59 +02:00
36192cbd89 Hold off on table-driven-tests for now.
I feel like that gets a bit too abstract, too quickly.
2026-05-20 11:03:09 +02:00
040e5aefe7 Implement PacketEngine struct and test 2026-05-19 12:59:16 +02:00
c6bbc531fc Update license 2026-05-19 10:48:57 +02:00
f4fcb46168 Restructure to library 2026-05-19 10:48:30 +02:00
e9a12e014e Update TOC 2026-05-19 10:48:30 +02:00
5b438698d4 Initial commit 2026-05-19 10:48:28 +02:00
8 changed files with 3934 additions and 2 deletions

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2026 m Copyright (c) 2026 Michael Smith
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including associated documentation files (the "Software"), to deal in the Software without restriction, including

View File

@@ -1,3 +1,5 @@
# go-agwpe # go-agwpe
Provides a client implementation of the AGWPE protocol for Go Provides a client implementation of the AGWPE protocol for Go.
Ported in part from https://github.com/mfncooper/pyham_pe.

66
agwpe.go Normal file
View File

@@ -0,0 +1,66 @@
package agwpe
import (
"fmt"
"net"
"sync"
"time"
)
type Config struct {
Timeout time.Duration
}
type Option func(*Config)
type PacketEngine struct {
address string
cfg Config
Ready bool
conn net.Conn
mu sync.Mutex
}
func WithTimeout(d time.Duration) Option {
return func(c *Config) {
c.Timeout = d
}
}
func NewPacketEngine(address string, opts ...Option) (*PacketEngine, error) {
defaultCfg := Config{
Timeout: 5 * time.Second,
}
for _, opt := range opts {
opt(&defaultCfg)
}
return &PacketEngine{
address: address,
cfg: defaultCfg,
Ready: false,
}, nil
}
func (pe *PacketEngine) Connect() error {
conn, err := net.DialTimeout("tcp", pe.address, pe.cfg.Timeout)
if err != nil {
return err
}
pe.conn = conn
fmt.Println("Connected to", pe.address)
return nil
}
func (pe *PacketEngine) Disconnect() error {
if pe.conn == nil {
return fmt.Errorf("Not connected!")
}
pe.conn.Close()
pe.conn = nil
fmt.Println("Disonnected from", pe.address)
return nil
}

79
agwpe_test.go Normal file
View File

@@ -0,0 +1,79 @@
package agwpe
import (
"net"
"testing"
"time"
)
// NOTE(m): Use more table-driven-tests? At the moment this feels less abstract.
func TestNewPacketEngine(t *testing.T) {
// Default configuration
engine, err := NewPacketEngine("localhost:1234")
if err != nil {
t.Errorf("Did not expect error: %v", err)
}
if engine.cfg.Timeout != 5*time.Second {
t.Errorf("Expected 5 second timeout, got %v", engine.cfg.Timeout)
}
if engine.Ready {
t.Error("Expected engine that is not ready")
}
// Custom timeout
engine, err = NewPacketEngine("localhost:1234", WithTimeout(30*time.Second))
if err != nil {
t.Errorf("Did not expect error: %v", err)
}
if engine.cfg.Timeout != 30*time.Second {
t.Errorf("Expected 30 second timeout, got %v", engine.cfg.Timeout)
}
}
func TestPacketEngine_Connect(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
serverAddr := listener.Addr().String()
// Successful connect
engine, _ := NewPacketEngine(serverAddr)
err = engine.Connect()
if err != nil {
t.Errorf("Did not expect error: %v", err)
}
// Invalid address
engine, _ = NewPacketEngine("invalid-address")
err = engine.Connect()
if err == nil {
t.Error("Expected an error")
}
}
func TestPacketEngine_Disconnect(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
serverAddr := listener.Addr().String()
engine, _ := NewPacketEngine(serverAddr)
engine.Connect()
// Disconnect connected engine
err = engine.Disconnect()
if err != nil {
t.Errorf("Did not expect error: %v", err)
}
// Call to Disconnect() on disconnected packet engine should return an error
err = engine.Disconnect()
if err == nil {
t.Error("Expected an error")
}
}

3734
docs/AGWPE_TCP_IP_API.md Normal file

File diff suppressed because it is too large Load Diff

35
frame.go Normal file
View File

@@ -0,0 +1,35 @@
package agwpe
import (
"encoding/binary"
)
type FrameHeader struct {
Port byte
Reserved0 [3]byte
DataKind byte
Reserved1 byte
PID byte
Reserved2 byte
CallFrom [10]byte
CallTo [10]byte
DataLen uint32
User [4]byte
}
func (f *FrameHeader) Serialize() []byte {
buf := make([]byte, 36)
buf[0] = f.Port
// Ignore reserved bytes 1 to 3
buf[4] = f.DataKind
// Ignore reserved byte 5
buf[6] = f.PID
// Ignore reserved byte 7
copy(buf[8:18], f.CallFrom[:])
copy(buf[18:28], f.CallTo[:])
binary.LittleEndian.PutUint32(buf[28:32], f.DataLen)
copy(buf[32:36], f.User[:])
return buf
}

13
frame_test.go Normal file
View File

@@ -0,0 +1,13 @@
package agwpe
import (
"testing"
)
func TestNewFrameHeader(t *testing.T) {
}
func TestFrameHeader_Serialize(t *testing.T) {
}

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module git.smith.eu/m/go-agwpe
go 1.24.5