WIP: Update struct decode and encode #2

Draft
Sirherobrine23 wants to merge 13 commits from struct-serealize into main
8 changed files with 59 additions and 470 deletions
Showing only changes of commit ac5a2cdff3 - Show all commits

View File

@@ -14,6 +14,7 @@ import (
"github.com/sandertv/go-raknet"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/pipe"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/structcode"
"sirherobrine23.org/Minecraft-Server/go-pproxit/proto"
)
@@ -52,7 +53,7 @@ func CreateClient(Addres []netip.AddrPort, Token [36]byte) (*Client, error) {
}
func (client *Client) Send(req proto.Request) error {
return proto.WriteRequest(client.Conn, req)
return structcode.NewEncode(client.Conn, req)
}
func (client *Client) Setup() error {
@@ -72,8 +73,8 @@ func (client *Client) Setup() error {
return err
}
res, err := proto.ReaderResponse(bytes.NewBuffer(buff[:n]))
if err != nil {
var res proto.Response
if err = structcode.NewDecode(bytes.NewBuffer(buff[:n]), &res); err != nil {
if opt, isOpt := err.(*net.OpError); isOpt {
if opt.Timeout() {
<-time.After(time.Second * 3)
@@ -133,8 +134,8 @@ func (client *Client) handlers() {
go client.Send(proto.Request{Ping: &now})
}
res, err := proto.ReaderResponse(bufioBuff)
var res proto.Response
err := structcode.NewDecode(bufioBuff, &res)
if err != nil {
fmt.Println(err)
if err == proto.ErrInvalidBody {
@@ -156,8 +157,8 @@ func (client *Client) handlers() {
var auth = proto.AgentAuth(client.Token)
for {
client.Send(proto.Request{AgentAuth: &auth})
res, err := proto.ReaderResponse(client.Conn)
if err != nil {
var res proto.Response
if err = structcode.NewDecode(client.Conn, &res); err != nil {
panic(err) // TODO: Require fix to agent shutdown graced
} else if res.Unauthorized {
return

View File

@@ -36,7 +36,7 @@ var CmdServer = cli.Command{
if err != nil {
return err
}
pproxitServer, err := server.NewController(calls, netip.AddrPortFrom(netip.IPv4Unspecified(), uint16(ctx.Int("port"))))
pproxitServer, err := server.NewController(calls, netip.AddrPortFrom(netip.IPv4Unspecified(), uint16(ctx.Int("port"))).String())
if err != nil {
return err
}

View File

@@ -1,85 +0,0 @@
package bigendian
import (
"encoding/binary"
"io"
)
func readStream[T any](r io.Reader) (data T, err error) {
err = binary.Read(r, binary.BigEndian, &data)
return
}
func writeStream[T any](w io.Writer, data T) error {
return binary.Write(w, binary.BigEndian, data)
}
func WriteBytes[T any](w io.Writer, value T) error {
return binary.Write(w, binary.BigEndian, value)
}
func ReaderBytes[T any](r io.Reader, value T, Size uint64) error {
return binary.Read(r, binary.BigEndian, value)
}
func ReadBytesN(r io.Reader, Size uint64) ([]byte, error) {
buff := make([]byte, Size)
if err := binary.Read(r, binary.BigEndian, buff); err != nil {
return nil, err
}
return buff, nil
}
func ReadInt8(r io.Reader) (int8, error) {
return readStream[int8](r)
}
func WriteInt8(w io.Writer, value int8) error {
return writeStream[int8](w, value)
}
func ReadInt16(r io.Reader) (int16, error) {
return readStream[int16](r)
}
func WriteInt16(w io.Writer, value int16) error {
return writeStream[int16](w, value)
}
func ReadInt32(r io.Reader) (int32, error) {
return readStream[int32](r)
}
func WriteInt32(w io.Writer, value int32) error {
return writeStream[int32](w, value)
}
func ReadInt64(r io.Reader) (int64, error) {
return readStream[int64](r)
}
func WriteInt64(w io.Writer, value int64) error {
return writeStream[int64](w, value)
}
func ReadUint8(r io.Reader) (uint8, error) {
return readStream[uint8](r)
}
func WriteUint8(w io.Writer, value uint8) error {
return writeStream[uint8](w, value)
}
func ReadUint16(r io.Reader) (uint16, error) {
return readStream[uint16](r)
}
func WriteUint16(w io.Writer, value uint16) error {
return writeStream[uint16](w, value)
}
func ReadUint32(r io.Reader) (uint32, error) {
return readStream[uint32](r)
}
func WriteUint32(w io.Writer, value uint32) error {
return writeStream[uint32](w, value)
}
func ReadUint64(r io.Reader) (uint64, error) {
return readStream[uint64](r)
}
func WriteUint64(w io.Writer, value uint64) error {
return writeStream[uint64](w, value)
}

View File

@@ -2,17 +2,13 @@ package proto
import (
"errors"
"fmt"
"io"
"net/netip"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/bigendian"
)
const (
ProtoTCP uint8 = 1 // TCP Protocol
ProtoUDP uint8 = 2 // UDP Protocol
ProtoBoth uint8 = 3 // TCP+UDP Protocol
ProtoBoth uint8 = iota // TCP+UDP Protocol
ProtoTCP uint8 = iota // TCP Protocol
ProtoUDP uint8 = iota // UDP Protocol
DataSize uint64 = 10_000 // Default listener data recive and send
PacketSize uint64 = 800 // Packet to without data only requests and response headers
@@ -28,86 +24,8 @@ type Client struct {
Proto uint8 // Protocol to close (proto.ProtoTCP, proto.ProtoUDP or proto.ProtoBoth)
}
func (close Client) Writer(w io.Writer) error {
addr := close.Client.Addr()
if !addr.IsValid() {
return fmt.Errorf("invalid ip address")
}
var family uint8 = 6
if addr.Is4() {
family = 4
}
if err := bigendian.WriteUint8(w, close.Proto); err != nil {
return err
} else if err := bigendian.WriteUint8(w, family); err != nil {
return err
} else if err := bigendian.WriteBytes(w, addr.AsSlice()); err != nil {
return err
} else if err := bigendian.WriteUint16(w, close.Client.Port()); err != nil {
return err
}
return nil
}
func (close *Client) Reader(r io.Reader) (err error) {
if close.Proto, err = bigendian.ReadUint8(r); err != nil {
return
}
family, err := bigendian.ReadUint8(r)
if err != nil {
return err
}
var addr netip.Addr
if family == 4 {
buff, err := bigendian.ReadBytesN(r, 4)
if err != nil {
return err
}
addr = netip.AddrFrom4([4]byte(buff))
} else {
buff, err := bigendian.ReadBytesN(r, 16)
if err != nil {
return err
}
addr = netip.AddrFrom16([16]byte(buff))
}
port, err := bigendian.ReadUint16(r)
if err != nil {
return err
}
close.Client = netip.AddrPortFrom(addr, port)
return
}
type ClientData struct {
Client Client // Client Destination
Size uint64 // Data size
Data []byte `json:"-"` // Bytes to send
}
func (data ClientData) Writer(w io.Writer) error {
if err := data.Client.Writer(w); err != nil {
return err
} else if err := bigendian.WriteUint64(w, data.Size); err != nil {
return err
} else if _, err := w.Write(data.Data[:data.Size]); err != nil { // Write data without convert to big-endian
return err
}
return nil
}
func (data *ClientData) Reader(r io.Reader) (err error) {
if err = data.Client.Reader(r); err != nil {
return
} else if data.Size, err = bigendian.ReadUint64(r); err != nil {
return
}
data.Data = make([]byte, data.Size)
if _, err = r.Read(data.Data); err != nil {
return
}
return
}

View File

@@ -1,19 +1,15 @@
package proto
import (
"bytes"
"errors"
"io"
"time"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/bigendian"
)
const (
ReqAuth uint64 = 1 // Request Agent Auth
ReqPing uint64 = 2 // Time ping
ReqCloseClient uint64 = 3 // Close client
ReqClientData uint64 = 4 // Send data
ReqAuth uint64 = iota // Request Agent Auth
ReqPing uint64 = iota // Time ping
ReqCloseClient uint64 = iota // Close client
ReqClientData uint64 = iota // Send data
)
var (
@@ -22,19 +18,6 @@ var (
type AgentAuth [36]byte
func (agent AgentAuth) Writer(w io.Writer) error {
if err := bigendian.WriteBytes(w, agent[:]); err != nil {
return err
}
return nil
}
func (agent *AgentAuth) Reader(r io.Reader) error {
if err := bigendian.ReaderBytes(r, agent[:], 36); err != nil {
return err
}
return nil
}
// Send request to agent and wait response
type Request struct {
AgentAuth *AgentAuth `json:",omitempty"` // Send agent authentication to controller
@@ -42,80 +25,3 @@ type Request struct {
ClientClose *Client `json:",omitempty"` // Close client in controller
DataTX *ClientData `json:",omitempty"` // Recive data from agent
}
func ReaderRequest(r io.Reader) (*Request, error) {
res := &Request{}
if err := res.Reader(r); err != nil {
return nil, err
}
return res, nil
}
func WriteRequest(w io.Writer, res Request) error {
buff, err := res.Wbytes()
if err != nil {
return err
} else if _, err := w.Write(buff); err != nil {
return err
}
return nil
}
// Get Bytes from Request
func (req Request) Wbytes() ([]byte, error) {
buff := new(bytes.Buffer)
if err := req.Writer(buff); err != nil {
return nil, err
}
return buff.Bytes(), nil
}
func (req Request) Writer(w io.Writer) error {
if auth := req.AgentAuth; auth != nil {
if err := bigendian.WriteUint64(w, ReqAuth); err != nil {
return err
}
return auth.Writer(w)
} else if ping := req.Ping; ping != nil {
if err := bigendian.WriteUint64(w, ReqPing); err != nil {
return err
}
return bigendian.WriteInt64(w, ping.UnixMilli())
} else if close := req.ClientClose; close != nil {
if err := bigendian.WriteUint64(w, ReqCloseClient); err != nil {
return err
}
return close.Writer(w)
} else if data := req.DataTX; data != nil {
if err := bigendian.WriteUint64(w, ReqClientData); err != nil {
return err
}
return data.Writer(w)
}
return ErrInvalidBody
}
func (req *Request) Reader(r io.Reader) (err error) {
var reqID uint64
if reqID, err = bigendian.ReadUint64(r); err != nil {
return
}
if reqID == ReqAuth {
req.AgentAuth = new(AgentAuth)
return req.AgentAuth.Reader(r)
} else if reqID == ReqPing {
var timeUnix int64
if timeUnix, err = bigendian.ReadInt64(r); err != nil {
return
}
req.Ping = new(time.Time)
*req.Ping = time.UnixMilli(timeUnix)
return
} else if reqID == ReqCloseClient {
req.ClientClose = new(Client)
return req.ClientClose.Reader(r)
} else if reqID == ReqClientData {
req.DataTX = new(ClientData)
return req.DataTX.Reader(r)
}
return ErrInvalidBody
}

View File

@@ -1,23 +1,19 @@
package proto
import (
"bytes"
"io"
"net/netip"
"time"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/bigendian"
)
const (
ResUnauthorized uint64 = 1 // Request not processed and ignored
ResBadRequest uint64 = 2 // Request cannot process and ignored
ResCloseClient uint64 = 3 // Controller closed connection
ResClientData uint64 = 4 // Controller accepted data
ResSendAuth uint64 = 5 // Send token to controller
ResAgentInfo uint64 = 6 // Agent info
ResPong uint64 = 7 // Ping response
ResNotListening uint64 = 8 // Resize buffer size
ResUnauthorized uint64 = iota // Request not processed and ignored
ResBadRequest uint64 = iota // Request cannot process and ignored
ResCloseClient uint64 = iota // Controller closed connection
ResClientData uint64 = iota // Controller accepted data
ResSendAuth uint64 = iota // Send token to controller
ResAgentInfo uint64 = iota // Agent info
ResPong uint64 = iota // Ping response
ResNotListening uint64 = iota // Resize buffer size
)
type AgentInfo struct {
@@ -26,65 +22,6 @@ type AgentInfo struct {
AddrPort netip.AddrPort // request address and port
}
func (agent AgentInfo) Writer(w io.Writer) error {
if err := bigendian.WriteUint8(w, agent.Protocol); err != nil {
return err
} else if err := bigendian.WriteUint16(w, agent.UDPPort); err != nil {
return err
} else if err := bigendian.WriteUint16(w, agent.TCPPort); err != nil {
return err
}
addr := agent.AddrPort.Addr()
if addr.Is4() {
if err := bigendian.WriteUint8(w, 4); err != nil {
return err
} else if err := bigendian.WriteBytes(w, addr.As4()); err != nil {
return err
}
} else {
if err := bigendian.WriteUint8(w, 6); err != nil {
return err
} else if err := bigendian.WriteBytes(w, addr.As16()); err != nil {
return err
}
}
if err := bigendian.WriteUint16(w, agent.AddrPort.Port()); err != nil {
return err
}
return nil
}
func (agent *AgentInfo) Reader(r io.Reader) (err error) {
if agent.Protocol, err = bigendian.ReadUint8(r); err != nil {
return
} else if agent.UDPPort, err = bigendian.ReadUint16(r); err != nil {
return
} else if agent.TCPPort, err = bigendian.ReadUint16(r); err != nil {
return
}
var addrFamily uint8
var addrPort uint16
var ipBytes []byte
if addrFamily, err = bigendian.ReadUint8(r); err != nil {
return
} else if addrFamily == 4 {
if ipBytes, err = bigendian.ReadBytesN(r, 4); err != nil {
return
}
} else if addrFamily == 6 {
if ipBytes, err = bigendian.ReadBytesN(r, 16); err != nil {
return
}
}
if addrPort, err = bigendian.ReadUint16(r); err != nil {
return
} else if len(ipBytes) == 16 {
agent.AddrPort = netip.AddrPortFrom(netip.AddrFrom16([16]byte(ipBytes)), addrPort)
} else {
agent.AddrPort = netip.AddrPortFrom(netip.AddrFrom4([4]byte(ipBytes)), addrPort)
}
return
}
// Reader data from Controller and process in agent
type Response struct {
Unauthorized bool `json:",omitempty"` // Controller reject connection
@@ -98,101 +35,3 @@ type Response struct {
CloseClient *Client `json:",omitempty"` // Controller end client
DataRX *ClientData `json:",omitempty"` // Controller recive data from client
}
func ReaderResponse(r io.Reader) (*Response, error) {
res := &Response{}
if err := res.Reader(r); err != nil {
return nil, err
}
return res, nil
}
func WriteResponse(w io.Writer, res Response) error {
buff, err := res.Wbytes()
if err != nil {
return err
} else if _, err := w.Write(buff); err != nil {
return err
}
return nil
}
// Get Bytes from Response
func (req Response) Wbytes() ([]byte, error) {
buff := new(bytes.Buffer)
if err := req.Writer(buff); err != nil {
return nil, err
}
return buff.Bytes(), nil
}
func (res Response) Writer(w io.Writer) error {
if res.Unauthorized {
return bigendian.WriteUint64(w, ResUnauthorized)
} else if res.BadRequest {
return bigendian.WriteUint64(w, ResBadRequest)
} else if res.SendAuth {
return bigendian.WriteUint64(w, ResSendAuth)
} else if res.NotListened {
return bigendian.WriteUint64(w, ResNotListening)
} else if pong := res.Pong; pong != nil {
if err := bigendian.WriteUint64(w, ResPong); err != nil {
return err
}
return bigendian.WriteInt64(w, pong.UnixMilli())
} else if closeClient := res.CloseClient; closeClient != nil {
if err := bigendian.WriteUint64(w, ResCloseClient); err != nil {
return err
}
return closeClient.Writer(w)
} else if rx := res.DataRX; rx != nil {
if err := bigendian.WriteUint64(w, ResClientData); err != nil {
return err
}
return rx.Writer(w)
} else if info := res.AgentInfo; info != nil {
if err := bigendian.WriteUint64(w, ResAgentInfo); err != nil {
return err
}
return info.Writer(w)
}
return ErrInvalidBody
}
func (res *Response) Reader(r io.Reader) error {
resID, err := bigendian.ReadUint64(r)
if err != nil {
return err
}
if resID == ResBadRequest {
res.BadRequest = true
return nil
} else if resID == ResUnauthorized {
res.Unauthorized = true
return nil
} else if resID == ResNotListening {
res.NotListened = true
return nil
} else if resID == ResSendAuth {
res.SendAuth = true
return nil
} else if resID == ResCloseClient {
res.CloseClient = new(Client)
return res.CloseClient.Reader(r)
} else if resID == ResClientData {
res.DataRX = new(ClientData)
return res.DataRX.Reader(r)
} else if resID == ResAgentInfo {
res.AgentInfo = new(AgentInfo)
return res.AgentInfo.Reader(r)
} else if resID == ResPong {
unixMil, err := bigendian.ReadInt64(r)
if err != nil {
return err
}
res.Pong = new(time.Time)
*res.Pong = time.UnixMilli(unixMil)
return nil
}
return ErrInvalidBody
}

View File

@@ -4,10 +4,10 @@ import (
"errors"
"fmt"
"net"
"net/netip"
"github.com/sandertv/go-raknet"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/structcode"
"sirherobrine23.org/Minecraft-Server/go-pproxit/proto"
)
@@ -27,11 +27,12 @@ type Server struct {
Agents map[string]*Tunnel
}
func NewController(calls ServerCall, local netip.AddrPort) (*Server, error) {
conn, err := raknet.Listen(local.String())
func NewController(calls ServerCall, local string) (*Server, error) {
conn, err := raknet.Listen(local)
if err != nil {
return nil, err
}
fmt.Printf("Listen on %s\n", conn.Addr().String())
tuns := &Server{
ControllConn: conn,
ControlCalls: calls,
@@ -55,36 +56,36 @@ func (controller *Server) handler() {
func (controller *Server) handlerConn(conn net.Conn) {
defer conn.Close() // End agent accepted
var req *proto.Request
var tunnelInfo TunnelInfo
var err error
for {
if req, err = proto.ReaderRequest(conn); err != nil {
var tunnelInfo TunnelInfo
var err error
var req proto.Request
if err = structcode.NewDecode(conn, &req); err != nil {
return
}
if req.AgentAuth == nil {
proto.WriteResponse(conn, proto.Response{SendAuth: true})
structcode.NewEncode(conn, proto.Response{SendAuth: true})
continue
} else if tunnelInfo, err = controller.ControlCalls.AgentAuthentication([36]byte(req.AgentAuth[:])); err != nil {
if err == ErrAuthAgentFail {
proto.WriteResponse(conn, proto.Response{Unauthorized: true})
structcode.NewEncode(conn, proto.Response{Unauthorized: true})
return
}
proto.WriteResponse(conn, proto.Response{BadRequest: true})
structcode.NewEncode(conn, proto.Response{BadRequest: true})
continue
}
// Close current tunnel
if tun, ok := controller.Agents[string(req.AgentAuth[:])]; ok {
fmt.Println("closing old tunnel")
tun.Close() // Close connection
}
var tun = &Tunnel{RootConn: conn, TunInfo: tunnelInfo, UDPClients: make(map[string]net.Conn), TCPClients: make(map[string]net.Conn)}
controller.Agents[string(req.AgentAuth[:])] = tun
tun.Setup()
delete(controller.Agents, string(req.AgentAuth[:]))
break
}
// Close current tunnel
if tun, ok := controller.Agents[string(req.AgentAuth[:])]; ok {
fmt.Println("closing old tunnel")
tun.Close() // Close connection
}
var tun = &Tunnel{RootConn: conn, TunInfo: tunnelInfo, UDPClients: make(map[string]net.Conn), TCPClients: make(map[string]net.Conn)}
controller.Agents[string(req.AgentAuth[:])] = tun
tun.Setup()
delete(controller.Agents, string(req.AgentAuth[:]))
}

View File

@@ -1,12 +1,16 @@
package server
import (
"bytes"
"fmt"
"io"
"log"
"net"
"net/netip"
"os"
"time"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/structcode"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/udplisterner"
"sirherobrine23.org/Minecraft-Server/go-pproxit/proto"
)
@@ -58,7 +62,12 @@ func (tun *Tunnel) Close() error {
}
func (tun *Tunnel) send(res proto.Response) error {
return proto.WriteResponse(tun.RootConn, res)
buff := new(bytes.Buffer)
if err := structcode.NewEncode(buff, res); err != nil {
return err
}
_, err := tun.RootConn.Write(buff.Bytes())
return err
}
type toWr struct {
@@ -118,11 +127,11 @@ func (tun *Tunnel) Setup() {
for {
log.Printf("waiting request from %s", tun.RootConn.RemoteAddr().String())
req, err := proto.ReaderRequest(tun.RootConn)
if err != nil {
var req proto.Request
if err := structcode.NewDecode(tun.RootConn, &req); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return
}
if req.AgentAuth != nil {
go tun.send(proto.Response{
AgentInfo: &proto.AgentInfo{