WIP: Update struct decode and encode #2
@ -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
2
go.mod
@ -1,3 +1,3 @@
|
||||
module sirherobrine23.com.br/Minecraft-Server/go-pproxit
|
||||
|
||||
go 1.22
|
||||
go 1.23
|
||||
|
@ -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()
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
const selectorTagName = "ser"
|
||||
|
||||
var (
|
||||
typeofError = reflect.TypeFor[error]()
|
||||
typeofBytes = reflect.TypeFor[[]byte]()
|
||||
|
||||
typeofTextUnmarshal = reflect.TypeFor[encoding.TextUnmarshaler]()
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
138
server.go
Normal 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)
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
212
server/tunnel.go
212
server/tunnel.go
@ -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
|
||||
}
|
@ -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
86
tun.go
Normal 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() {}
|
Reference in New Issue
Block a user