WIP: Update struct decode and encode #2

Draft
Sirherobrine23 wants to merge 13 commits from struct-serealize into main
25 changed files with 1035 additions and 1464 deletions
Showing only changes of commit 0b280040fc - Show all commits

View File

@ -1,12 +1,13 @@
package client
package gopproxit
import (
"errors"
"fmt"
"io"
"maps"
"net"
"net/netip"
"reflect"
"slices"
"time"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/pipe"
@ -14,13 +15,6 @@ import (
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
)
var ErrUnauthorized error = errors.New("cannot auth to controller")
type remoteClient struct {
Client proto.Client
Conn net.Conn
}
type toWr struct {
Proto proto.Protoc
To netip.AddrPort
@ -44,26 +38,32 @@ func (t toWr) Write(w []byte) (int, error) {
}
type Client struct {
Token []byte // Token to auth in Controller if required
Conn net.Conn // Connection from Controller
Agent *proto.AgentInfo // Agent info to show in UI and listened on controller
TCPConns map[string]*net.TCPConn // Clients connections to TCP
UDPConns map[string]*net.UDPConn // Clients connections to UDP
LastPong time.Time // Last Pong time
Latency int64 // Latency response in ms from last Pong
Token []byte // Token to auth in Controller if required
Conn net.Conn // Connection from Controller
Agent *proto.AgentInfo // Agent info to show in UI and listened on controller
LastPong time.Time // Last Pong time
Latency int64 // Latency response in ms from last Pong
newListen chan remoteClient // new clients listener in Controller
errListen chan error // new clients listener in Controller
TCPConns map[string]*net.TCPConn // Clients connections to TCP
UDPConns map[string]*net.UDPConn // Clients connections to UDP
newListenTCP, newListenUDP chan net.Conn // Channel to accept new connections
errListen chan error // new clients listener in Controller
}
func NewClient(Address string, AuthToken []byte) (*Client, error) {
var clientStr Client
var err error
// Dial connection
if clientStr.Conn, err = net.Dial("tcp", Address); err != nil {
// Dial TCP Connection and return Client
func NewTCPClient(Address string, AuthToken []byte) (*Client, error) {
conn, err := net.Dial("tcp", Address)
if err != nil {
return nil, err
}
return NewClient(conn, AuthToken)
}
// Setup net.Conn and return client
func NewClient(conn net.Conn, AuthToken []byte) (*Client, error) {
var clientStr Client
clientStr.Conn = conn
var res proto.Response
if err := structcode.NewEncode(clientStr.Conn, proto.Request{Ping: proto.Point(time.Now())}); err != nil {
@ -71,7 +71,7 @@ func NewClient(Address string, AuthToken []byte) (*Client, error) {
} else if err := structcode.NewDecode(clientStr.Conn, &res); err != nil {
return nil, err
} else if res.SendAuth && len(AuthToken) == 0 {
return nil, ErrUnauthorized
return nil, ErrAuthUnauthorized
}
// Auth Session
@ -82,52 +82,43 @@ func NewClient(Address string, AuthToken []byte) (*Client, error) {
}
}
clientStr.newListenTCP, clientStr.newListenUDP = make(chan net.Conn), make(chan net.Conn)
clientStr.TCPConns, clientStr.UDPConns = make(map[string]*net.TCPConn), make(map[string]*net.UDPConn)
clientStr.errListen = make(chan error)
clientStr.newListen = make(chan remoteClient)
clientStr.TCPConns = make(map[string]*net.TCPConn)
clientStr.UDPConns = make(map[string]*net.UDPConn)
go clientStr.handle() // Process requests
return &clientStr, nil
}
// Wait for any error
func (client *Client) WaitError() error {
if err, ok := <-client.errListen; ok {
return err
}
return io.EOF
}
// Return addr from Dial
func (Client *Client) Addr() net.Addr { return Client.Conn.RemoteAddr() }
// Wait for any error, if catch error close connection and return error
func (client *Client) WaitCloseError() error {
err := client.WaitError()
if err != nil && client.Conn != nil {
client.Close()
// Accept connection from Controller
func (client *Client) Accept() (net.Conn, error) {
select {
case v := <-client.newListenTCP:
return v, nil
case v := <-client.newListenUDP:
return v, nil
case err := <-client.errListen:
return nil, err
}
return err
}
// Close Clients and Controller connection
func (client *Client) Close() error {
for remoteClient := range client.TCPConns {
d := client.TCPConns[remoteClient]
if err := d.Close(); err != nil {
return err
}
}
for remoteClient := range client.UDPConns {
d := client.UDPConns[remoteClient]
if err := d.Close(); err != nil {
return err
}
}
if client.Conn != nil {
if err := client.Conn.Close(); err != nil {
return err
}
client.Conn = nil
client.Conn.Close()
}
close(client.newListen)
for _, d := range slices.Collect(maps.Values(client.TCPConns)) {
d.Close()
}
for _, d := range slices.Collect(maps.Values(client.UDPConns)) {
d.Close()
}
close(client.newListenTCP)
close(client.newListenUDP)
close(client.errListen)
return nil
}
@ -143,14 +134,14 @@ func (client *Client) sendErr(err error) {
// Send auth to controller
func (client *Client) Auth() error {
if err := structcode.NewEncode(client.Conn, proto.Request{AgentAuth: &client.Token}); err != nil {
if err := structcode.NewEncode(client.Conn, proto.Request{AgentAuth: client.Token}); err != nil {
return err
}
var res proto.Response
if err := structcode.NewDecode(client.Conn, &res); err != nil {
return err
} else if res.BadRequest || res.Unauthorized {
return ErrUnauthorized
return ErrAuthUnauthorized
} else if res.AgentInfo == nil {
return fmt.Errorf("cannot get agent info")
}
@ -176,7 +167,7 @@ func (client *Client) handle() {
}
continue
} else if res.Unauthorized {
client.sendErr(ErrUnauthorized) // Close connection
client.sendErr(ErrAuthUnauthorized) // Close connection
return
}
@ -191,35 +182,6 @@ func (client *Client) handle() {
client.LastPong = *res.Pong
}
// Write data to Client
if data := res.DataRX; res.DataRX != nil {
var ok bool
var clientConn net.Conn
clientAddr := res.DataRX.Client.Client.String()
switch res.DataRX.Client.Proto {
case proto.ProtoTCP:
if clientConn, ok = client.TCPConns[clientAddr]; !ok {
toClient, toAgent := pipe.CreatePipe(data.Client.Client, data.Client.Client)
go io.Copy(&toWr{data.Client.Proto, data.Client.Client, client}, toAgent)
client.newListen <- remoteClient{res.DataRX.Client, toClient}
clientConn = client.TCPConns[clientAddr]
}
case proto.ProtoUDP:
if clientConn, ok = client.UDPConns[clientAddr]; !ok {
toClient, toAgent := pipe.CreatePipe(data.Client.Client, data.Client.Client)
go io.Copy(&toWr{data.Client.Proto, data.Client.Client, client}, toAgent)
client.newListen <- remoteClient{res.DataRX.Client, toClient}
clientConn = client.UDPConns[clientAddr]
}
default:
continue
}
if _, err := clientConn.Write(res.DataRX.Data); err != nil {
client.sendErr(err)
continue
}
}
if res.CloseClient != nil {
clientAddr := res.DataRX.Client.Client.String()
switch res.DataRX.Client.Proto {
@ -239,13 +201,35 @@ func (client *Client) handle() {
}
}
}
}
}
// Accept connection from Controller
func (client *Client) Accept() (proto.Protoc, net.Conn, error) {
if conn, ok := <-client.newListen; ok {
return conn.Client.Proto, conn.Conn, nil
// Write data to Client
if data := res.DataRX; res.DataRX != nil {
var ok bool
var clientConn net.Conn
clientAddr := res.DataRX.Client.Client.String()
switch res.DataRX.Client.Proto {
case proto.ProtoTCP:
if clientConn, ok = client.TCPConns[clientAddr]; !ok {
toClient, toAgent := pipe.CreatePipe(data.Client.Client, data.Client.Client)
go io.Copy(&toWr{data.Client.Proto, data.Client.Client, client}, toAgent)
client.newListenTCP <- toClient
clientConn = client.TCPConns[clientAddr]
}
if _, err := clientConn.Write(res.DataRX.Data); err != nil {
client.sendErr(err)
}
case proto.ProtoUDP:
if clientConn, ok = client.UDPConns[clientAddr]; !ok {
toClient, toAgent := pipe.CreatePipe(data.Client.Client, data.Client.Client)
go io.Copy(&toWr{data.Client.Proto, data.Client.Client, client}, toAgent)
client.newListenUDP <- toClient
clientConn = client.UDPConns[clientAddr]
}
if _, err := clientConn.Write(res.DataRX.Data); err != nil {
client.sendErr(err)
}
}
}
}
return 0, nil, io.EOF
}

2
go.mod
View File

@ -1,3 +1,3 @@
module sirherobrine23.com.br/Minecraft-Server/go-pproxit
go 1.22
go 1.23

View File

@ -1,30 +0,0 @@
package gopproxit_test
import (
"testing"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/client"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/server"
)
func TestClientServer(t *testing.T) {
calls, err := NewCall()
if err != nil {
t.Error(err)
return
}
controller, err := server.NewController(calls, 8881)
if err != nil {
t.Error(err)
return
}
defer controller.ControllConn.Close()
token := calls.RegisterRandomUser()
clientConn, err := client.NewClient(controller.ControllConn.Addr().String(), token)
if err != nil {
t.Error(err)
return
}
defer clientConn.Close()
}

View File

@ -24,6 +24,9 @@ func decodeTypeof(r io.Reader, reflectValue reflect.Value) (bool, error) {
switch {
default:
return false, nil
case reflectValue.Type().Implements(typeofError), reflectValue.Type().ConvertibleTo(typeofError):
readBuff(r) // Read buff and ignore
return false, nil
case typeof.Implements(typeofBinUnmarshal), typeof.ConvertibleTo(typeofBinUnmarshal):
buff, err := readBuff(r)
if err != nil {

View File

@ -25,6 +25,8 @@ func encodeTypeof(w io.Writer, reflectValue reflect.Value) (bool, error) {
data, err = reflectValue.Interface().(encoding.BinaryMarshaler).MarshalBinary()
case reflectValue.Type().Implements(typeofTextMarshal), reflectValue.Type().ConvertibleTo(typeofTextMarshal):
data, err = reflectValue.Interface().(encoding.TextMarshaler).MarshalText()
case reflectValue.Type().Implements(typeofError), reflectValue.Type().ConvertibleTo(typeofError):
data = []byte(reflectValue.Interface().(error).Error())
}
if err == nil {
err = writeBuff(w, data)

View File

@ -27,6 +27,7 @@ import (
const selectorTagName = "ser"
var (
typeofError = reflect.TypeFor[error]()
typeofBytes = reflect.TypeFor[[]byte]()
typeofTextUnmarshal = reflect.TypeFor[encoding.TextUnmarshaler]()

View File

@ -138,7 +138,7 @@ func TestSerelelize(t *testing.T) {
t.Run("Request", func(t *testing.T) {
var err error
var encodeRequest, decodeRequest proto.Request
encodeRequest.AgentAuth = &[]byte{0, 0, 1, 1, 1, 1, 1, 0, 255}
encodeRequest.AgentAuth = []byte{0, 0, 1, 1, 1, 1, 1, 0, 255}
encodeRequest.Ping = new(time.Time)
*encodeRequest.Ping = time.Now()
@ -165,8 +165,8 @@ func TestSerelelize(t *testing.T) {
} else if decodeRequest.Ping.Unix() != encodeRequest.Ping.Unix() {
t.Errorf("cannot decode/encode Ping date, Decode %d, Encode: %d", decodeRequest.Ping.Unix(), encodeRequest.Ping.Unix())
return
} else if !bytes.Equal(*decodeRequest.AgentAuth, *encodeRequest.AgentAuth) {
t.Errorf("cannot decode/encode auth data, Decode %q, Encode: %q", hex.EncodeToString(*decodeRequest.AgentAuth), hex.EncodeToString(*encodeRequest.AgentAuth))
} else if !bytes.Equal(decodeRequest.AgentAuth, encodeRequest.AgentAuth) {
t.Errorf("cannot decode/encode auth data, Decode %q, Encode: %q", hex.EncodeToString(decodeRequest.AgentAuth), hex.EncodeToString(encodeRequest.AgentAuth))
return
}
})

View File

@ -9,10 +9,6 @@ const (
ProtoBoth Protoc = iota // TCP+UDP Protocol
ProtoTCP // TCP Protocol
ProtoUDP // UDP Protocol
DataSize uint64 = 10_000 // Default listener data recive and send
PacketSize uint64 = 800 // Packet to without data only requests and response headers
PacketDataSize uint64 = DataSize + PacketSize // Header and Data request/response
)
var ErrInvalidBody error = errors.New("invalid body, check request/response")
@ -34,8 +30,8 @@ func (pr Protoc) String() string {
}
type Client struct {
Client netip.AddrPort // Client address and port
Proto Protoc // Protocol to close (proto.ProtoTCP, proto.ProtoUDP or proto.ProtoBoth)
Client netip.AddrPort // Client address and port
}
type ClientData struct {

View File

@ -9,8 +9,8 @@ var ErrProtoBothNoSupported error = errors.New("protocol UDP+TCP not supported c
// Send request to agent and wait response
type Request struct {
AgentAuth []byte `json:",omitempty"` // Send agent authentication to controller
Ping *time.Time `json:",omitempty"` // Send ping time to controller in unix milliseconds
AgentAuth *[]byte `json:",omitempty"` // Send agent authentication to controller
ClientClose *Client `json:",omitempty"` // Close client in controller
DataTX *ClientData `json:",omitempty"` // Recive data from agent
}

View File

@ -8,19 +8,20 @@ import (
type AgentInfo struct {
Protocol Protoc // Proto supported (proto.ProtoTCP, proto.ProtoUDP or proto.ProtoBoth)
UDPPort, TCPPort uint16 // Controller port listened
AddrPort netip.AddrPort // request address and port
AddrPort netip.AddrPort // Address and port
ConnectToken []byte // Bytes to conenct
}
// Reader data from Controller and process in agent
type Response struct {
Unauthorized bool `json:",omitempty"` // Controller reject connection
BadRequest bool `json:",omitempty"` // Controller accepted packet so cannot process Request
SendAuth bool `json:",omitempty"` // Send Agent token
NotListened bool `json:",omitempty"` // Controller cannot Listen port
AgentInfo *AgentInfo `json:",omitempty"` // Agent Info
Pong *time.Time `json:",omitempty"` // ping response
CloseClient *Client `json:",omitempty"` // Controller end client
DataRX *ClientData `json:",omitempty"` // Controller recive data from client
Unauthorized bool `json:",omitempty"` // Controller reject connection
SendAuth bool `json:",omitempty"` // Send Agent token
BadRequest bool `json:",omitempty"` // Controller accepted packet so cannot process Request
NotListened bool `json:",omitempty"` // Controller cannot Listen port
AgentInfo *AgentInfo `json:",omitempty"` // Agent Info
Pong *time.Time `json:",omitempty"` // ping response
ConnectTo *netip.AddrPort `json:",omitempty"` // Connect to dial and auth
CloseClient *Client `json:",omitempty"` // Controller end client
DataRX *ClientData `json:",omitempty"` // Controller recive data from client
Error error `json:",omitempty"` // Error
}

138
server.go Normal file
View File

@ -0,0 +1,138 @@
package gopproxit
import (
"encoding/hex"
"errors"
"io"
"net"
"net/netip"
"reflect"
"sync"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/structcode"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
)
var (
ErrAuthInvalidToken error = errors.New("invalid token authentication") // Token is bad
ErrAuthUnauthorized error = errors.New("unauthorized authentication") // Token not exists
)
type ServerServices interface {
Auth(token []byte) error // Authenticate client
Agent(token []byte) (*proto.AgentInfo, error) // Informations to agent
SkipAddress(addr netip.AddrPort) bool // Close/skip address process
}
type Server struct {
Controller net.Listener // Controller connection
Services ServerServices // Services to auth and another options
tunLock sync.Locker
Tuns map[string]*Tunnel
errChannel chan error
}
func NewServer(addr string, service ServerServices) (*Server, error) {
var server Server
var err error
if server.Controller, err = net.Listen("tcp", addr); err != nil {
return nil, err
}
server.Services = service
server.errChannel = make(chan error)
server.tunLock = &sync.Mutex{}
go server.accepts()
return &server, nil
}
// Send error to WaitError, if closed catcher and ignored
func (server *Server) sendErr(err error) {
if reflect.ValueOf(server.errChannel).IsZero() {
return
}
defer func() { recover() }()
server.errChannel <- err
}
// process new connections
func (server *Server) accepts() {
for server.Controller != nil {
conn, err := server.Controller.Accept()
if err != nil {
server.sendErr(err)
return
} else if server.Services.SkipAddress(netip.MustParseAddrPort(conn.RemoteAddr().String())) {
conn.Close()
continue
}
go func(conn net.Conn) {
defer conn.Close()
var req proto.Request
i := 0
for {
if i++; i > 5 {
return
} else if err := structcode.NewDecode(conn, &req); err != nil {
if err == io.EOF {
return
}
server.sendErr(err)
continue
} else if err := server.Services.Auth(req.AgentAuth); err != nil {
if err == ErrAuthUnauthorized {
structcode.NewEncode(conn, proto.Response{Unauthorized: true})
return
} else if err == ErrAuthInvalidToken {
structcode.NewEncode(conn, proto.Response{Unauthorized: true})
return
}
structcode.NewEncode(conn, proto.Response{BadRequest: true})
server.sendErr(err)
continue
}
break
}
info, err := server.Services.Agent(req.AgentAuth)
if err != nil {
structcode.NewEncode(conn, proto.Response{Error: err})
server.sendErr(err)
conn.Close()
return
} else if err := structcode.NewEncode(conn, proto.Response{AgentInfo: info}); err != nil {
server.sendErr(err)
conn.Close()
return
}
tokenHex := hex.EncodeToString(req.AgentAuth)
server.tunLock.Lock()
defer conn.Close()
tun := NewTun(conn, info)
if oldTun, ok := server.Tuns[tokenHex]; ok {
oldTun.Close()
delete(server.Tuns, tokenHex)
}
server.Tuns[tokenHex] = tun
server.tunLock.Unlock()
defer func() {
server.tunLock.Lock()
defer server.tunLock.Unlock()
delete(server.Tuns, tokenHex)
}()
for {
select {
case err := <-tun.TunErr:
server.sendErr(err)
case <-tun.Done:
return
}
}
}(conn)
}
}

View File

@ -1,92 +0,0 @@
package server
import (
"errors"
"fmt"
"io"
"net"
"net/netip"
"os"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/structcode"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
)
var ErrAuthAgentFail error = errors.New("cannot authenticate agent") // Send unathorized client and close new accepts from current port
type ServerCall interface {
// Authenticate agents
AgentAuthentication(Token []byte) (TunnelInfo, error)
}
type Server struct {
ControllConn net.Listener
ProcessError chan error
ControlCalls ServerCall
Agents map[string]*Tunnel
}
func NewController(calls ServerCall, port uint16) (*Server, error) {
conn, err := net.ListenTCP("tcp", net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), port)))
if err != nil {
return nil, err
}
fmt.Printf("Listen on %s\n", conn.Addr().String())
tuns := &Server{
ControllConn: conn,
ControlCalls: calls,
Agents: make(map[string]*Tunnel),
ProcessError: make(chan error),
}
go tuns.handler()
return tuns, nil
}
func (controller *Server) handler() {
defer controller.ControllConn.Close()
for {
conn, err := controller.ControllConn.Accept()
if err != nil {
break
}
fmt.Printf("New Client %q\n", conn.RemoteAddr())
go controller.handlerConn(conn)
}
}
func (controller *Server) handlerConn(conn net.Conn) {
defer conn.Close()
var tunnelInfo TunnelInfo
var err error
var req proto.Request
for {
if err = structcode.NewDecode(conn, &req); err != nil {
if err != io.EOF {
fmt.Fprintf(os.Stderr, "Auth decode error: %s\n", err.Error())
}
return
}
if tunnelInfo, err = controller.ControlCalls.AgentAuthentication(*req.AgentAuth); err != nil {
if err == ErrAuthAgentFail {
structcode.NewEncode(conn, proto.Response{Unauthorized: true})
return
}
structcode.NewEncode(conn, proto.Response{BadRequest: true})
continue
}
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,212 +0,0 @@
package server
import (
"fmt"
"io"
"net"
"net/netip"
"os"
"time"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/structcode"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/udplisterner"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
)
type TunnelCall interface {
BlockedAddr(AddrPort string) bool // Ignore request from this address
AgentPing(agent, server time.Time) // Register ping to Agent
AgentShutdown(onTime time.Time) // Agend end connection
RegisterRX(client netip.AddrPort, Size int, Proto proto.Protoc) // Register Recived data from client
RegisterTX(client netip.AddrPort, Size int, Proto proto.Protoc) // Register Transmitted data from client
}
type TunnelInfo struct {
Proto proto.Protoc // Protocol listen tunnel, use proto.ProtoTCP, proto.ProtoUDP or proto.ProtoBoth
UDPPort, TCPPort uint16 // Port to Listen UDP and TCP listeners
Callbacks TunnelCall // Tunnel Callbacks
}
type Tunnel struct {
RootConn net.Conn // Current client connection
TunInfo TunnelInfo // Tunnel info
connTCP *net.TCPListener
connUDP net.Listener
UDPClients map[string]net.Conn // Current clients connected
TCPClients map[string]net.Conn // Current clients connected
}
func (tun *Tunnel) Close() error {
tun.connTCP.Close()
tun.connUDP.Close()
// Stop TCP Clients
for k := range tun.TCPClients {
tun.TCPClients[k].Close()
delete(tun.TCPClients, k)
}
// Stop UDP Clients
for k := range tun.UDPClients {
tun.UDPClients[k].Close()
delete(tun.UDPClients, k)
}
go tun.RootConn.Close() // End root conenction
go tun.TunInfo.Callbacks.AgentShutdown(time.Now()) // Register shutdown
return nil
}
func (tun *Tunnel) send(res proto.Response) error {
return structcode.NewEncode(tun.RootConn, res)
}
type toWr struct {
Proto proto.Protoc
To netip.AddrPort
tun *Tunnel
}
func (t toWr) Write(w []byte) (int, error) {
go t.tun.TunInfo.Callbacks.RegisterRX(t.To, len(w), t.Proto)
err := t.tun.send(proto.Response{
DataRX: &proto.ClientData{
Data: w,
Client: proto.Client{
Proto: t.Proto,
Client: t.To,
},
},
})
if err == nil {
return len(w), nil
}
return 0, err
}
func (tun *Tunnel) GetTargetWrite(Proto proto.Protoc, To netip.AddrPort) io.Writer {
return &toWr{Proto: Proto, To: To, tun: tun}
}
// Setup connections and maneger connections from agent
func (tun *Tunnel) Setup() {
if proto.ProtoBoth == tun.TunInfo.Proto || proto.ProtoTCP == tun.TunInfo.Proto {
// Setup TCP Listerner
if err := tun.TCP(); err != nil {
tun.send(proto.Response{NotListened: true})
return
}
}
if proto.ProtoBoth == tun.TunInfo.Proto || proto.ProtoUDP == tun.TunInfo.Proto {
// Setup UDP Listerner
if err := tun.UDP(); err != nil {
tun.send(proto.Response{NotListened: true})
return
}
}
defer tun.Close()
tun.send(proto.Response{
AgentInfo: &proto.AgentInfo{
Protocol: tun.TunInfo.Proto,
AddrPort: netip.MustParseAddrPort(tun.RootConn.RemoteAddr().String()),
UDPPort: tun.TunInfo.UDPPort,
TCPPort: tun.TunInfo.TCPPort,
},
})
for {
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{
Protocol: tun.TunInfo.Proto,
AddrPort: netip.MustParseAddrPort(tun.RootConn.RemoteAddr().String()),
UDPPort: tun.TunInfo.UDPPort,
TCPPort: tun.TunInfo.TCPPort,
},
})
continue
} else if ping := req.Ping; req.Ping != nil {
var now = time.Now()
tun.send(proto.Response{Pong: &now})
go tun.TunInfo.Callbacks.AgentPing(*ping, now) // backgroud process
} else if clClose := req.ClientClose; req.ClientClose != nil {
if clClose.Proto == proto.ProtoTCP {
if cl, ok := tun.TCPClients[clClose.Client.String()]; ok {
cl.Close()
}
} else if clClose.Proto == proto.ProtoUDP {
if cl, ok := tun.UDPClients[clClose.Client.String()]; ok {
cl.Close()
}
}
} else if data := req.DataTX; req.DataTX != nil {
go tun.TunInfo.Callbacks.RegisterTX(data.Client.Client, len(data.Data), data.Client.Proto)
if data.Client.Proto == proto.ProtoTCP {
if cl, ok := tun.TCPClients[data.Client.Client.String()]; ok {
go cl.Write(data.Data) // Process in backgroud
}
} else if data.Client.Proto == proto.ProtoUDP {
if cl, ok := tun.UDPClients[data.Client.Client.String()]; ok {
go cl.Write(data.Data) // Process in backgroud
}
}
}
}
}
// Listen TCP
func (tun *Tunnel) TCP() (err error) {
if tun.connTCP, err = net.ListenTCP("tcp", net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), tun.TunInfo.TCPPort))); err != nil {
return err
}
go func() {
for {
conn, err := tun.connTCP.AcceptTCP()
if err != nil {
// panic(err) // TODO: fix accepts in future
return
}
remote := netip.MustParseAddrPort(conn.RemoteAddr().String())
if tun.TunInfo.Callbacks.BlockedAddr(remote.Addr().String()) {
conn.Close() // Close connection
continue
}
tun.TCPClients[remote.String()] = conn
go io.Copy(tun.GetTargetWrite(proto.ProtoTCP, remote), conn)
}
}()
return nil
}
// Listen UDP
func (tun *Tunnel) UDP() (err error) {
if tun.connUDP, err = udplisterner.ListenAddrPort("udp", netip.AddrPortFrom(netip.IPv4Unspecified(), tun.TunInfo.UDPPort)); err != nil {
return
}
go func() {
for {
conn, err := tun.connUDP.Accept()
if err != nil {
// panic(err) // TODO: fix accepts in future
return
}
remote := netip.MustParseAddrPort(conn.RemoteAddr().String())
if tun.TunInfo.Callbacks.BlockedAddr(remote.Addr().String()) {
conn.Close() // Close connection
continue
}
tun.UDPClients[remote.String()] = conn
go io.Copy(tun.GetTargetWrite(proto.ProtoUDP, remote), conn)
}
}()
return
}

View File

@ -1,107 +0,0 @@
package gopproxit_test
import (
"net/netip"
"sync"
"time"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/server"
)
type serverCalls struct {
Locker sync.Locker
Tables map[string]map[int64]any
}
type User struct {
ID int64 // Client ID
Username string // Username
FullName string // Real name for user
AccountStatus int8 // Account Status
CreateAt time.Time // Create date
UpdateAt time.Time // Update date
}
type Tun struct {
ID int64 // Tunnel ID
User int64 // Agent ID
Token []byte // Tunnel Token
Proto proto.Protoc // Proto accept
TPCListen uint16 // Port listen TCP agent
UDPListen uint16 // Port listen UDP agent
}
type Ping struct {
ID int64 `json:"-"` // Tunnel ID
TunID int64 `json:"-"`
ServerTime time.Time `json:"server"`
AgentTime time.Time `json:"agent"`
}
type AddrBlocked struct {
ID int64 `json:"-"` // Tunnel ID
TunID int64 `json:"-"`
Enabled bool
Address string
}
type RTX struct {
ID int64 `json:"-"` // Tunnel ID
TunID int64 `json:"-"`
Client netip.AddrPort
TXSize int
RXSize int
Proto proto.Protoc
}
func NewCall() (call *serverCalls, err error) {
call = new(serverCalls)
call.Locker = &sync.Mutex{}
call.Tables = make(map[string]map[int64]any)
call.Tables["User"] = make(map[int64]any)
call.Tables["Tun"] = make(map[int64]any)
call.Tables["AddrBlocked"] = make(map[int64]any)
call.Tables["Ping"] = make(map[int64]any)
call.Tables["RTX"] = make(map[int64]any)
return
}
type TunCallbcks struct {
tunID int64
Locker sync.Locker
}
func (tun *TunCallbcks) BlockedAddr(AddrPort string) bool { return false }
func (*TunCallbcks) AgentShutdown(onTime time.Time) {}
func (tun *TunCallbcks) AgentPing(agent, server time.Time) {}
func (tun *TunCallbcks) RegisterRX(client netip.AddrPort, Size int, Proto proto.Protoc) {}
func (tun *TunCallbcks) RegisterTX(client netip.AddrPort, Size int, Proto proto.Protoc) {}
func (caller *serverCalls) AgentAuthentication(Token []byte) (server.TunnelInfo, error) {
for _, tunInfo := range caller.Tables["Tun"] {
return server.TunnelInfo{
Proto: tunInfo.(Tun).Proto,
TCPPort: tunInfo.(Tun).TPCListen,
UDPPort: tunInfo.(Tun).UDPListen,
Callbacks: &TunCallbcks{
Locker: caller.Locker,
tunID: tunInfo.(Tun).ID,
},
}, nil
}
return server.TunnelInfo{}, server.ErrAuthAgentFail
}
func (caller *serverCalls) RegisterRandomUser() []byte {
token := []byte{0, 0, 12, 14, 22, 89, 255, 81}
caller.Tables["User"][0] = User{ID: 0, AccountStatus: 1, FullName: "Radon user", Username: "random"}
caller.Tables["Tun"][0] = Tun{
User: 0,
Token: token,
Proto: proto.ProtoBoth,
TPCListen: 5522,
UDPListen: 5522,
}
return token
}

86
tun.go Normal file
View File

@ -0,0 +1,86 @@
package gopproxit
import (
"maps"
"net"
"net/netip"
"reflect"
"slices"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
)
type Tunnel struct {
Controller net.Conn // Tunnel client connection
Agent *proto.AgentInfo // Agent info
TunErr chan error // Return errors
Done chan struct{} // Closed connection
TCPServer *net.TCPListener
UDPServer *net.UDPConn
TCPConns map[string]net.Conn
UDPConns map[string]net.Conn
}
func NewTun(conn net.Conn, Agent *proto.AgentInfo) *Tunnel {
var tun Tunnel
tun.Agent = Agent
tun.Controller = conn
// Listen clients
tun.TCPConns, tun.UDPConns = make(map[string]net.Conn), make(map[string]net.Conn)
tun.TunErr = make(chan error)
tun.Done = make(chan struct{})
if Agent.AddrPort.Compare(netip.AddrPortFrom(netip.IPv4Unspecified(), 0)) == 0 {
switch Agent.Protocol {
case proto.ProtoTCP:
go tun.TCPServerhandler()
case proto.ProtoUDP:
go tun.UDPServerhandler()
case proto.ProtoBoth:
go tun.TCPServerhandler()
go tun.UDPServerhandler()
}
}
return &tun
}
func (tun *Tunnel) Close() error {
if tun.Controller != nil {
tun.Controller.Close()
}
for _, d := range slices.Collect(maps.Values(tun.TCPConns)) {
d.Close()
}
for _, d := range slices.Collect(maps.Values(tun.UDPConns)) {
d.Close()
}
tun.Done <- struct{}{}
close(tun.Done)
close(tun.TunErr)
return nil
}
// Send error to WaitError, if closed catcher and ignored
func (tun *Tunnel) sendErr(err error) {
if reflect.ValueOf(tun.TunErr).IsZero() {
return
}
defer func() { recover() }()
tun.TunErr <- err
}
func (tun *Tunnel) Handler() {}
func (tun *Tunnel) TCPServerhandler() {
var err error
if tun.TCPServer, err = net.ListenTCP("tcp", net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), tun.Agent.TCPPort))); err != nil {
tun.sendErr(err)
return
}
}
func (tun *Tunnel) UDPServerhandler() {}