Update struct decode and encode #2

Open
Sirherobrine23 wants to merge 13 commits from struct-serealize into main
26 changed files with 1212 additions and 1494 deletions

15
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,15 @@
name: Test
on:
pull_request:
jobs:
Test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
- name: Go test
run: go test ./...

View File

@ -1,21 +1,15 @@
# Golang pproxit to TCP and UDP Connections
Same to playit.gg, to make more simples to connections and implementations and host self server proxy
Same to playit.gg, to make more simples to connections and implementations and host self server proxy, this implements only Server, for client Wrapper to same `net.Listener`
## TODO
## Server
- [ ] Agent Connect
- [x] Auth
- [ ] Send shutdow agent
- [x] Recive packets
- [x] Send packets
- [ ] Controller
- [x] Listener tunnels
- [x] Redirect packets to agent
- Require Patchs to fix agent retransmitter packets
- [ ] TCP
- [ ] TX Data
- [ ] RX Data
- [ ] UDP
- [ ] TX Data
- [ ] RX Data
Controls all connections and connected clients, any request within the accepted posts will be forwarded to its current clients without any data filtering, only users that can be blocked
## Client
Connect to the controller and accept and manage local connections, quickly and dynamically, without having to specify the values
### Auth
As part of being simple, the controller can enable simple or even more complex authentication, and some data can also be recorded if there are some functions enabled by the Controller.

235
client.go Normal file
View File

@ -0,0 +1,235 @@
package gopproxit
import (
"fmt"
"io"
"maps"
"net"
"net/netip"
"reflect"
"slices"
"time"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/pipe"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/structcode"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
)
type toWr struct {
Proto proto.Protoc
To netip.AddrPort
tun *Client
}
func (t toWr) Write(w []byte) (int, error) {
data := proto.Request{
DataTX: &proto.ClientData{
Data: w,
Client: proto.Client{
Client: t.To,
Proto: t.Proto,
},
},
}
if err := structcode.NewEncode(t.tun.Conn, data); err != nil {
return 0, err
}
return len(w), nil
}
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
LastPong time.Time // Last Pong time
Latency int64 // Latency response in ms from last Pong
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
}
// 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 {
return nil, err
} else if err := structcode.NewDecode(clientStr.Conn, &res); err != nil {
return nil, err
} else if res.SendAuth && len(AuthToken) == 0 {
return nil, ErrAuthUnauthorized
}
// Auth Session
if clientStr.Token = AuthToken; len(clientStr.Token) > 0 {
if err := clientStr.Auth(); err != nil {
clientStr.Conn.Close()
return nil, err
}
}
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)
go clientStr.handle() // Process requests
return &clientStr, nil
}
// Return addr from Dial
func (Client *Client) Addr() net.Addr { return Client.Conn.RemoteAddr() }
// 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
}
}
// Close Clients and Controller connection
func (client *Client) Close() error {
if client.Conn != nil {
client.Conn.Close()
}
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
}
// Send error to WaitError, if closed catcher and ignored
func (client *Client) sendErr(err error) {
if reflect.ValueOf(client.errListen).IsZero() {
return
}
defer func() { recover() }()
client.errListen <- err
}
// Send auth to controller
func (client *Client) Auth() error {
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 ErrAuthUnauthorized
} else if res.AgentInfo == nil {
return fmt.Errorf("cannot get agent info")
}
client.Agent = res.AgentInfo
return nil
}
// Process Controller responses
func (client *Client) handle() {
for {
var res proto.Response
if err := structcode.NewDecode(client.Conn, &res); err != nil {
client.sendErr(err)
return
}
// Resend auth to Controller
if res.SendAuth {
if err := client.Auth(); err != nil {
client.sendErr(err) // Send to handler
return
}
continue
} else if res.Unauthorized {
client.sendErr(ErrAuthUnauthorized) // Close connection
return
}
// Update current agent info
if res.AgentInfo != nil {
client.Agent = res.AgentInfo
}
// Server pong time
if res.Pong != nil {
client.Latency = time.Now().UnixMilli() - res.Pong.UnixMilli()
client.LastPong = *res.Pong
}
if res.CloseClient != nil {
clientAddr := res.DataRX.Client.Client.String()
switch res.DataRX.Client.Proto {
case proto.ProtoTCP:
if clientConn, ok := client.TCPConns[clientAddr]; ok {
delete(client.TCPConns, clientAddr)
if err := clientConn.Close(); err != nil {
client.sendErr(err)
}
}
case proto.ProtoUDP:
if clientConn, ok := client.UDPConns[clientAddr]; ok {
delete(client.UDPConns, clientAddr)
if err := clientConn.Close(); err != nil {
client.sendErr(err)
}
}
}
}
// 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)
}
}
}
}
}

View File

@ -1,221 +0,0 @@
package client
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/netip"
"time"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/pipe"
"sirherobrine23.org/Minecraft-Server/go-pproxit/proto"
)
var (
ErrCannotConnect error = errors.New("cannot connect to controller")
)
type NewClient struct {
Client proto.Client
Writer net.Conn
}
type Client struct {
Token [36]byte
RemoteAdress []netip.AddrPort
clientsTCP map[string]net.Conn
clientsUDP map[string]net.Conn
NewClient chan NewClient
Conn *net.UDPConn
AgentInfo *proto.AgentInfo
}
func CreateClient(Addres []netip.AddrPort, Token [36]byte) (*Client, error) {
cli := &Client{
Token: Token,
RemoteAdress: Addres,
clientsTCP: make(map[string]net.Conn),
clientsUDP: make(map[string]net.Conn),
NewClient: make(chan NewClient),
}
if err := cli.Setup(); err != nil {
return cli, err
}
return cli, nil
}
func (client *Client) Send(req proto.Request) error {
return proto.WriteRequest(client.Conn, req)
}
func (client *Client) Setup() error {
for _, addr := range client.RemoteAdress {
var err error
if client.Conn, err = net.DialUDP("udp", nil, net.UDPAddrFromAddrPort(addr)); err != nil {
continue
}
client.Conn.SetReadDeadline(time.Now().Add(time.Second * 5))
var auth = proto.AgentAuth(client.Token)
for {
client.Send(proto.Request{AgentAuth: &auth})
buff := make([]byte, 1024)
n, err := client.Conn.Read(buff)
if err != nil {
return err
}
res, err := proto.ReaderResponse(bytes.NewBuffer(buff[:n]))
if err != nil {
if opt, isOpt := err.(*net.OpError); isOpt {
if opt.Timeout() {
<-time.After(time.Second * 3)
client.Send(proto.Request{AgentAuth: &auth})
continue
}
}
// return err
break
} else if res.Unauthorized {
return ErrCannotConnect
} else if res.AgentInfo == nil {
continue
}
client.AgentInfo = res.AgentInfo
client.Conn.SetReadDeadline(*new(time.Time)) // clear timeout
go client.handlers()
return nil
}
}
return ErrCannotConnect
}
type toWr struct {
Proto uint8
To netip.AddrPort
tun *Client
}
func (t toWr) Write(w []byte) (int, error) {
err := t.tun.Send(proto.Request{
DataTX: &proto.ClientData{
Client: proto.Client{
Client: t.To,
Proto: t.Proto,
},
Size: uint64(len(w)),
Data: w[:],
},
})
if err == nil {
return len(w), nil
}
return 0, err
}
func (tun *Client) GetTargetWrite(Proto uint8, To netip.AddrPort) io.Writer {
return &toWr{Proto: Proto, To: To, tun: tun}
}
func (client *Client) handlers() {
bufioBuff := bufio.NewReader(client.Conn)
var lastPing int64 = 0
for {
if time.Now().UnixMilli()-lastPing > 3_000 {
var now = time.Now()
go client.Send(proto.Request{Ping: &now})
}
res, err := proto.ReaderResponse(bufioBuff)
if err != nil {
fmt.Println(err)
if err == proto.ErrInvalidBody {
continue
}
panic(err) // TODO: Require fix to agent shutdown graced
}
d, _ := json.Marshal(res)
fmt.Println(string(d))
if res.Pong != nil {
lastPing = res.Pong.UnixMilli()
continue
}
if res.Unauthorized || res.NotListened {
panic(fmt.Errorf("cannot recive requests")) // TODO: Require fix to agent shutdown graced
} else if res.SendAuth {
var auth = proto.AgentAuth(client.Token)
for {
client.Send(proto.Request{AgentAuth: &auth})
res, err := proto.ReaderResponse(client.Conn)
if err != nil {
panic(err) // TODO: Require fix to agent shutdown graced
} else if res.Unauthorized {
return
} else if res.AgentInfo == nil {
continue
}
client.AgentInfo = res.AgentInfo
break
}
} else if cl := res.CloseClient; res.CloseClient != nil {
if cl.Proto == proto.ProtoTCP {
if tun, ok := client.clientsTCP[cl.Client.String()]; ok {
tun.Close()
}
} else if cl.Proto == proto.ProtoUDP {
if tun, ok := client.clientsUDP[cl.Client.String()]; ok {
tun.Close()
}
}
} else if data := res.DataRX; res.DataRX != nil {
if data.Client.Proto == proto.ProtoTCP {
if _, ok := client.clientsTCP[data.Client.Client.String()]; !ok {
toClient, toAgent := pipe.CreatePipe(net.TCPAddrFromAddrPort(data.Client.Client), net.TCPAddrFromAddrPort(data.Client.Client))
client.NewClient <- NewClient{
Client: data.Client,
Writer: toClient,
}
client.clientsTCP[data.Client.Client.String()] = toAgent
go func() {
io.Copy(client.GetTargetWrite(proto.ProtoTCP, data.Client.Client), toAgent)
delete(client.clientsTCP, data.Client.Client.String())
}()
}
} else if data.Client.Proto == proto.ProtoUDP {
if _, ok := client.clientsUDP[data.Client.Client.String()]; !ok {
toClient, toAgent := pipe.CreatePipe(net.UDPAddrFromAddrPort(data.Client.Client), net.UDPAddrFromAddrPort(data.Client.Client))
client.NewClient <- NewClient{
Client: data.Client,
Writer: toClient,
}
client.clientsUDP[data.Client.Client.String()] = toAgent
go func() {
io.Copy(client.GetTargetWrite(proto.ProtoUDP, data.Client.Client), toAgent)
delete(client.clientsUDP, data.Client.Client.String())
toAgent.Close()
}()
}
}
if data.Client.Proto == proto.ProtoTCP {
if tun, ok := client.clientsTCP[data.Client.Client.String()]; ok {
go tun.Write(data.Data)
}
} else if data.Client.Proto == proto.ProtoUDP {
if tun, ok := client.clientsUDP[data.Client.Client.String()]; ok {
go tun.Write(data.Data)
}
} else if res.Pong != nil {
fmt.Println(res.Pong.String())
}
}
}
}

View File

@ -1,82 +0,0 @@
package client
import (
"fmt"
"io"
"net"
"net/netip"
"github.com/google/uuid"
"github.com/urfave/cli/v2"
"sirherobrine23.org/Minecraft-Server/go-pproxit/client"
"sirherobrine23.org/Minecraft-Server/go-pproxit/proto"
)
var CmdClient = cli.Command{
Name: "client",
Aliases: []string{"c"},
Usage: "connect to controller server and bind new requests to local port",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "url",
Required: true,
Aliases: []string{"host", "u"},
Usage: `host string to connect to controller, example: "example.com:5522"`,
},
&cli.StringFlag{
Name: "token",
Required: true,
Usage: "agent token",
Aliases: []string{"t"},
Action: func(ctx *cli.Context, s string) error {
if _, err := uuid.Parse(s); err == nil {
return nil
} else if len(s) == len(proto.AgentAuth{}) {
return nil
}
return fmt.Errorf("set valid token")
},
},
&cli.StringFlag{
Name: "dial",
Required: true,
Usage: `dial connection, default is "localhost:80"`,
Aliases: []string{"d"},
},
},
Action: func(ctx *cli.Context) (err error) {
var addr netip.AddrPort
if addr, err = netip.ParseAddrPort(ctx.String("url")); err != nil {
return
}
client, err := client.CreateClient([]netip.AddrPort{addr}, [36]byte([]byte(ctx.String("token"))))
if err != nil {
return err
}
fmt.Printf("Connected, Remote address: %s\n", client.AgentInfo.AddrPort.String())
if client.AgentInfo.Protocol == proto.ProtoUDP {
fmt.Printf(" Port: UDP %d\n", client.AgentInfo.UDPPort)
} else if client.AgentInfo.Protocol == proto.ProtoTCP {
fmt.Printf(" Port: TCP %d\n", client.AgentInfo.TCPPort)
} else if client.AgentInfo.Protocol == proto.ProtoBoth {
fmt.Printf(" Ports UDP %d and TCP %d\n", client.AgentInfo.UDPPort, client.AgentInfo.TCPPort)
}
localConnect := ctx.String("dial")
for {
client := <-client.NewClient
var dial net.Conn
if client.Client.Proto == proto.ProtoTCP {
if dial, err = net.Dial("tcp", localConnect); err != nil {
continue
}
} else {
if dial, err = net.Dial("udp", localConnect); err != nil {
continue
}
}
go io.Copy(client.Writer, dial)
go io.Copy(dial, client.Writer)
}
},
}

View File

@ -1,28 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/urfave/cli/v2"
"sirherobrine23.org/Minecraft-Server/go-pproxit/cmd/client"
"sirherobrine23.org/Minecraft-Server/go-pproxit/cmd/server"
)
var description string = `pproxit is a proxy that allows anyone to host a server without port forwarding. We use tunneling. Only the server needs to run the program, not every player!`
func main() {
app := cli.NewApp()
app.Name = "pproxit"
app.Description = description
app.EnableBashCompletion = true
app.HideHelpCommand = true
app.Commands = []*cli.Command{
&server.CmdServer,
&client.CmdClient,
}
if err := app.Run(os.Args); err != nil {
fmt.Fprintf(app.ErrWriter, "Error: %v\n", err)
os.Exit(1)
}
}

View File

@ -1,45 +0,0 @@
package server
import (
"net/netip"
"github.com/urfave/cli/v2"
"sirherobrine23.org/Minecraft-Server/go-pproxit/server"
)
var CmdServer = cli.Command{
Name: "server",
Usage: "Create local server and open controller ports",
Aliases: []string{"s"},
Flags: []cli.Flag{
&cli.IntFlag{
Name: "port",
Value: 5522,
Aliases: []string{"p"},
Usage: "Set controller port to watcher UDP requests",
},
&cli.StringFlag{
Name: "log",
Value: "silence",
Aliases: []string{"l"},
Usage: "set server log: silence, 0 or verbose, 2",
},
&cli.StringFlag{
Name: "db",
Value: "./pproxit.db",
Aliases: []string{"d"},
Usage: "sqlite file path",
},
},
Action: func(ctx *cli.Context) error {
calls, err := NewCall(ctx.String("db"))
if err != nil {
return err
}
pproxitServer, err := server.NewController(calls, netip.AddrPortFrom(netip.IPv4Unspecified(), uint16(ctx.Int("port"))))
if err != nil {
return err
}
return <-pproxitServer.ProcessError
},
}

View File

@ -1,147 +0,0 @@
package server
import (
"fmt"
"net/netip"
"time"
_ "modernc.org/sqlite"
"sirherobrine23.org/Minecraft-Server/go-pproxit/server"
"xorm.io/xorm"
"xorm.io/xorm/names"
)
type serverCalls struct {
XormEngine *xorm.Engine
}
type User struct {
ID int64 `xorm:"pk"` // Client ID
Username string `xorm:"varchar(32) notnull unique 'user'"` // Username
FullName string `xorm:"text notnull notnull 'name'"` // Real name for user
AccountStatus int8 `xorm:"BIT notnull 'status'"` // Account Status
CreateAt time.Time `xorm:"created"` // Create date
UpdateAt time.Time `xorm:"updated"` // Update date
}
type Tun struct {
ID int64 `xorm:"pk"` // Tunnel ID
User int64 `xorm:"notnull"` // Agent ID
Token [36]byte `xorm:"blob notnull unique"` // Tunnel Token
Proto uint8 `xorm:"default 3"` // Proto accept
TPCListen uint16 // Port listen TCP agent
UDPListen uint16 // Port listen UDP agent
}
type Ping struct {
ID int64 `json:"-" xorm:"pk"` // Tunnel ID
TunID int64 `json:"-"`
ServerTime time.Time `json:"server" xorm:"datetime notnull"`
AgentTime time.Time `json:"agent" xorm:"datetime notnull"`
}
type AddrBlocked struct {
ID int64 `json:"-" xorm:"pk"` // Tunnel ID
TunID int64 `json:"-"`
Enabled bool
Address string
}
type RTX struct {
ID int64 `json:"-" xorm:"pk"` // Tunnel ID
TunID int64 `json:"-"`
Client netip.AddrPort
TXSize int
RXSize int
Proto uint8
}
func NewCall(DBConn string) (call *serverCalls, err error) {
call = new(serverCalls)
if call.XormEngine, err = xorm.NewEngine("sqlite", DBConn); err != nil {
return
}
call.XormEngine.SetMapper(names.SameMapper{})
session := call.XormEngine.NewSession()
defer session.Close()
session.CreateTable(User{})
session.CreateTable(Tun{})
session.CreateTable(AddrBlocked{})
session.CreateTable(Ping{})
session.CreateTable(RTX{})
return
}
type TunCallbcks struct {
tunID int64
XormEngine *xorm.Engine
}
func (tun *TunCallbcks) AgentShutdown(onTime time.Time) {}
func (tun *TunCallbcks) BlockedAddr(AddrPort string) bool {
var addr = AddrBlocked{Address: AddrPort, TunID: tun.tunID}
ok, err := tun.XormEngine.Get(&addr)
if err != nil {
fmt.Println(err)
return true
} else if ok {
return addr.Enabled
}
var addrs []AddrBlocked
if err := tun.XormEngine.Find(&addrs); err != nil {
fmt.Println(err)
return true
}
for ind := range addrs {
if addrs[ind].Enabled {
return true
}
}
return false
}
func (tun *TunCallbcks) AgentPing(agent, server time.Time) {
c, _ := tun.XormEngine.Count(Ping{})
tun.XormEngine.InsertOne(&Ping{
ID: c,
TunID: tun.tunID,
ServerTime: server,
AgentTime: agent,
})
}
func (tun *TunCallbcks) RegisterRX(client netip.AddrPort, Size int, Proto uint8) {
tun.XormEngine.InsertOne(&RTX{
TunID: tun.tunID,
Client: client,
Proto: Proto,
RXSize: Size,
TXSize: 0,
})
}
func (tun *TunCallbcks) RegisterTX(client netip.AddrPort, Size int, Proto uint8) {
tun.XormEngine.InsertOne(&RTX{
TunID: tun.tunID,
Client: client,
Proto: Proto,
TXSize: Size,
RXSize: 0,
})
}
func (caller *serverCalls) AgentAuthentication(Token [36]byte) (server.TunnelInfo, error) {
var tun = Tun{Token: Token}
if ok, err := caller.XormEngine.Get(&tun); err != nil || !ok {
if !ok {
return server.TunnelInfo{}, server.ErrAuthAgentFail
}
return server.TunnelInfo{}, err
}
return server.TunnelInfo{
Proto: tun.Proto,
TCPPort: tun.TPCListen,
UDPPort: tun.UDPListen,
Callbacks: &TunCallbcks{tunID: tun.ID, XormEngine: caller.XormEngine},
}, nil
}

30
docs/proto_pt-br.md Normal file
View File

@ -0,0 +1,30 @@
# Protocolo
- Client: Está modulo pode usar qual abstração do [`net.Conn`](https://pkg.go.dev/net#Conn) para conectar a um controlador como cliente
- Servidor/Controlador: a mesma coisa se estabelece para o controlador, basta passar um [`net.Listener`](https://pkg.go.dev/net#Listener) para processar novos clientes
- As struct's estão serelealizadas em BINARIO com [Big-Endian](https://pt.wikipedia.org/wiki/Extremidade_(ordena%C3%A7%C3%A3o)#Hardware_bi-endian)
```mermaid
graph LR
A(Cliente)
A --> B{Ping}
B --> |Não necessita do token|C[Pong] --> D(Cliente aguarda novas respostas do Controlador)
B --> E[SendAuth] -->|Enviar token| G{Verificar token}
G -->|Ok|C
G -->|Invalid token - 5 tentativas|E
G --x|Not exists/Max attempts|H(Fechar conexão)
```
## Cliente
- O Cliente dever manda um ping iniciamente para receber uma respostas, caso sejá requisitado uma altenticação, re-envia uma resposta para o controlador, o controlador deve apenas aceita as primeiras 5 tentativas de autenticação.
- O cliente deve sempre está escutando está conexão para processar as struct's.
- Valores invalidos devem ser ignorados por questão de vazamento de dados ou estouro de memoria.
## Servidor/Controlador
- O servidores devem receber um ping para checar se necessita de autenticação, caso neccesario mande uma respota `SendAuth` para que o cliente mande a chave de autenticação.
- Na etapa de autenticação, aceita apenas as primeiras 5 requisições do cliente, depois disso deve ser ENCERRADO a conexão sem possivel explicação para o cliente.
- O serviço que será implementado devera implementa uma Autenticação e uma forma para aceitar conexões UDP e TCP de forma parecida com o [`net.Listener`](https://pkg.go.dev/net#Listener), caso ao contrario será ignorado e não será aceito qualquer novos clients desta forma.
- Quando um cliente desconectar ou de forma inesperada será encaminha para o cliente que a conexão foi encerada.
- (Rascunho de implementação) O controlador não informarar quando um novo cliente será aceito pelo controlador, apenas enviarar o primeiros bytes recebidos por ele.

36
go.mod
View File

@ -1,35 +1,3 @@
module sirherobrine23.org/Minecraft-Server/go-pproxit
module sirherobrine23.com.br/Minecraft-Server/go-pproxit
go 1.22.3
require (
github.com/google/uuid v1.6.0
github.com/urfave/cli/v2 v2.27.2
modernc.org/sqlite v1.30.1
xorm.io/xorm v1.3.9
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/goccy/go-json v0.8.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
golang.org/x/sys v0.19.0 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.52.1 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
)
go 1.23

113
go.sum
View File

@ -1,113 +0,0 @@
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.17.10 h1:6wrtRozgrhCxieCeJh85QsxkX/2FFrT9hdaWPlbn4Zo=
modernc.org/ccgo/v4 v4.17.10/go.mod h1:0NBHgsqTTpm9cA5z2ccErvGZmtntSM9qD2kFAs6pjXM=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.52.1 h1:uau0VoiT5hnR+SpoWekCKbLqm7v6dhRL3hI+NQhgN3M=
modernc.org/libc v1.52.1/go.mod h1:HR4nVzFDSDizP620zcMCgjb1/8xk2lg5p/8yjfGv1IQ=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU=
xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=

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

@ -7,6 +7,7 @@ package pipe
import (
"io"
"net"
"net/netip"
"os"
"sync"
"time"
@ -81,7 +82,7 @@ func isClosedChan(c <-chan struct{}) bool {
}
type pipe struct {
localAddr, remoteAddr net.Addr
localAddr, remoteAddr netip.AddrPort
wrMu sync.Mutex // Serialize Write operations
@ -108,7 +109,7 @@ type pipe struct {
// Reads on one end are matched with writes on the other,
// copying data directly between the two; there is no internal
// buffering.
func CreatePipe(LocalAddress, RemoteAddress net.Addr) (net.Conn, net.Conn) {
func CreatePipe(LocalAddress, RemoteAddress netip.AddrPort) (net.Conn, net.Conn) {
cb1 := make(chan []byte)
cb2 := make(chan []byte)
cn1 := make(chan int)
@ -139,8 +140,11 @@ func CreatePipe(LocalAddress, RemoteAddress net.Addr) (net.Conn, net.Conn) {
return p1, p2
}
func (p *pipe) LocalAddr() net.Addr { return p.localAddr }
func (p *pipe) RemoteAddr() net.Addr { return p.remoteAddr }
type addr struct{ netip.AddrPort }
func (addr) Network() string { return "pipe" }
func (p *pipe) LocalAddr() net.Addr { return addr{p.localAddr} }
func (p *pipe) RemoteAddr() net.Addr { return addr{p.remoteAddr} }
func (p *pipe) Read(b []byte) (int, error) {
n, err := p.read(b)

View File

@ -0,0 +1,15 @@
package structcode
import (
"bytes"
)
func Marshal(target any) ([]byte, error) {
buff := new(bytes.Buffer)
err := NewEncode(buff, target)
return buff.Bytes(), err
}
func Unmarshal(b []byte, target any) error {
return NewDecode(bytes.NewReader(b), target)
}

View File

@ -0,0 +1,136 @@
package structcode
import (
"encoding"
"encoding/binary"
"io"
"reflect"
)
func readBuff(r io.Reader) ([]byte, error) {
size := uint32(0)
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return nil, err
}
buff := make([]byte, size)
_, err := r.Read(buff)
return buff, err
}
func decodeTypeof(r io.Reader, reflectValue reflect.Value) (bool, error) {
var data any
npoint := reflect.New(reflectValue.Type())
typeof := npoint.Type()
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 {
return true, err
}
t := npoint.Interface()
if err := t.(encoding.BinaryUnmarshaler).UnmarshalBinary(buff); err != nil {
return true, err
}
data = t
case typeof.Implements(typeofTextUnmarshal), typeof.ConvertibleTo(typeofTextUnmarshal):
buff, err := readBuff(r)
if err != nil {
return true, err
}
t := npoint.Interface()
if err := t.(encoding.TextUnmarshaler).UnmarshalText(buff); err != nil {
return true, err
}
data = t
}
reflectValue.Set(reflect.ValueOf(data).Elem())
return true, nil
}
func decodeRecursive(r io.Reader, reflectValue reflect.Value) error {
switch reflectValue.Type().Kind() {
case reflect.String:
buff, err := readBuff(r)
if err != nil {
return err
}
reflectValue.SetString(string(buff))
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
data := reflect.New(reflectValue.Type()).Interface()
if err := binary.Read(r, binary.BigEndian, data); err != nil {
return err
}
reflectValue.Set(reflect.ValueOf(data).Elem())
case reflect.Interface:
case reflect.Map:
mapTypeof := reflectValue.Type()
reflectValue.Set(reflect.MakeMap(mapTypeof))
var size uint64
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return err
}
for range size {
key, value := reflect.New(mapTypeof.Key()).Elem(), reflect.New(mapTypeof.Elem()).Elem()
if err := decodeRecursive(r, key); err != nil {
return err
} else if err := decodeRecursive(r, value); err != nil {
return err
}
reflectValue.SetMapIndex(key, value)
}
case reflect.Struct:
if ok, err := decodeTypeof(r, reflectValue); ok {
return err
}
typeof := reflectValue.Type()
for fieldIndex := range typeof.NumField() {
fieldType := typeof.Field(fieldIndex)
if fieldType.Tag.Get(selectorTagName) == "-" || !fieldType.IsExported() {
continue
} else if err := decodeRecursive(r, reflectValue.Field(fieldIndex)); err != nil {
return err
}
}
case reflect.Pointer:
var read bool
if err := binary.Read(r, binary.BigEndian, &read); err != nil {
return err
} else if read {
reflectValue.Set(reflect.New(reflectValue.Type().Elem()))
return decodeRecursive(r, reflectValue.Elem())
}
case reflect.Array:
for arrIndex := range reflectValue.Len() {
if err := decodeRecursive(r, reflectValue.Index(arrIndex)); err != nil {
return err
}
}
case reflect.Slice:
if reflectValue.Type().ConvertibleTo(typeofBytes) {
buff, err := readBuff(r)
if err != nil {
return err
}
reflectValue.SetBytes(buff)
return nil
}
size := int64(0)
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return err
}
typeof := reflectValue.Type().Elem()
for range size {
newData := reflect.New(typeof)
if err := decodeRecursive(r, newData); err != nil {
return err
}
reflectValue.Set(reflect.AppendSlice(reflectValue, newData.Elem()))
}
}
return nil
}

View File

@ -0,0 +1,98 @@
package structcode
import (
"encoding"
"encoding/binary"
"io"
"reflect"
)
func writeBuff(w io.Writer, buff []byte) error {
if err := binary.Write(w, binary.BigEndian, uint32(len(buff))); err != nil {
return err
}
_, err := w.Write(buff)
return err
}
func encodeTypeof(w io.Writer, reflectValue reflect.Value) (bool, error) {
var err error = nil
var data []byte
switch {
default:
return false, nil
case reflectValue.Type().Implements(typeofBinMarshal), reflectValue.Type().ConvertibleTo(typeofBinMarshal):
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)
}
return true, err
}
func encodeRecursive(w io.Writer, reflectValue reflect.Value) error {
switch reflectValue.Type().Kind() {
case reflect.String:
return writeBuff(w, []byte(reflectValue.String()))
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return binary.Write(w, binary.BigEndian, reflectValue.Interface())
case reflect.Interface:
case reflect.Map:
if err := binary.Write(w, binary.BigEndian, uint64(reflectValue.Len())); err != nil {
return err
}
inter := reflectValue.MapRange()
for inter.Next() {
if err := encodeRecursive(w, inter.Key()); err != nil {
return err
} else if err := encodeRecursive(w, inter.Value()); err != nil {
return err
}
}
case reflect.Struct:
if ok, err := encodeTypeof(w, reflectValue); ok {
return err
}
typeof := reflectValue.Type()
for fieldIndex := range typeof.NumField() {
fieldType := typeof.Field(fieldIndex)
if fieldType.Tag.Get(selectorTagName) == "-" || !fieldType.IsExported() {
continue
} else if err := encodeRecursive(w, reflectValue.Field(fieldIndex)); err != nil {
return err
}
}
case reflect.Pointer:
if reflectValue.IsNil() || reflectValue.IsZero() {
return binary.Write(w, binary.BigEndian, false)
} else if err := binary.Write(w, binary.BigEndian, true); err != nil {
return err
}
return encodeRecursive(w, reflectValue.Elem())
case reflect.Array:
for arrIndex := range reflectValue.Len() {
if err := encodeRecursive(w, reflectValue.Index(arrIndex)); err != nil {
return err
}
}
case reflect.Slice:
if reflectValue.Type().ConvertibleTo(typeofBytes) {
return writeBuff(w, reflectValue.Bytes())
} else if err := binary.Write(w, binary.BigEndian, int64(reflectValue.Len())); err != nil {
return err
} else if reflectValue.Type().Elem().Kind() == typeofBytes.Elem().Kind() {
_, err = w.Write(reflectValue.Bytes())
return err
}
for sliceIndex := range reflectValue.Len() {
if err := encodeRecursive(w, reflectValue.Index(sliceIndex)); err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,77 @@
/*
Este bloco de codigo server apenas para facilitar minha vida no mundo do go
Definindo a estrutura de seguinte maneira, os dados estão fortemente ligado as structs go então qualquer merda aqui pode ferra com qualquer versão anterior, então isso não será recomendado para algumas coisa
Os dados serão escritos em BigEndian, então tenha cuidado com os dados inseridos casos sejam inportantes
Os pointers serão verificados se são nil's para idicar para a sereliazação e desereliazação
*any -> int8(0|1) + data...
[]any -> int64 (Size) + data...
[]bytes Or String -> Int64 (Size) + []bytes
map[any]any -> int64 (Size) + (key + data)...
int64, uint64 -> int64
int32, uint32 -> int32
int16, uint16 -> int16
int8, uint8 -> int8
bool -> int8(0|1)
*/
package structcode
import (
"encoding"
"errors"
"fmt"
"io"
"reflect"
)
const selectorTagName = "ser"
var (
typeofError = reflect.TypeFor[error]()
typeofBytes = reflect.TypeFor[[]byte]()
typeofTextUnmarshal = reflect.TypeFor[encoding.TextUnmarshaler]()
typeofBinUnmarshal = reflect.TypeFor[encoding.BinaryUnmarshaler]()
typeofTextMarshal = reflect.TypeFor[encoding.TextMarshaler]()
typeofBinMarshal = reflect.TypeFor[encoding.BinaryMarshaler]()
)
func NewEncode(w io.Writer, target any) (err error) {
if target == nil {
return nil
}
defer func() {
if ierr := recover(); ierr != nil {
switch v := ierr.(type) {
case error:
err = v
case string:
err = errors.New(v)
}
}
}()
reflectValue := reflect.ValueOf(target)
if reflectValue.Type().Kind() == reflect.Pointer {
reflectValue = reflectValue.Elem()
}
return encodeRecursive(w, reflectValue)
}
func NewDecode(r io.Reader, target any) (err error) {
defer func() {
if ierr := recover(); ierr != nil {
switch v := ierr.(type) {
case error:
err = v
case string:
err = errors.New(v)
}
}
}()
if target == nil {
return fmt.Errorf("set target, not nil")
} else if reflect.TypeOf(target).Kind() != reflect.Pointer {
return fmt.Errorf("set pointer to struct")
}
return decodeRecursive(r, reflect.ValueOf(target).Elem())
}

View File

@ -0,0 +1,173 @@
package structcode
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
mathrand "math/rand/v2"
"net/netip"
"sync"
"testing"
"time"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/proto"
)
type mapTest map[string]mapStr
type mapStr struct {
Text string
Blob []byte
}
func randomBuff(size int) []byte {
buff := make([]byte, size)
_, err := rand.Read(buff)
if err != nil {
panic(err)
}
return buff
}
func TestSerelelize(t *testing.T) {
t.Run("Map", func(t *testing.T) {
var err error
var waiter sync.WaitGroup
var enc, dec mapTest
enc = mapTest{}
enc["Test"] = mapStr{"Golang is best", []byte{5, 14, 22, 13}}
for i := range mathrand.IntN(20) {
enc["Rand"+fmt.Sprint(i)] = mapStr{string(randomBuff(14)), randomBuff(64)}
}
waiter.Add(2)
r, w := io.Pipe()
go func() {
defer waiter.Done()
if err = NewDecode(r, &dec); err != nil {
t.Error(err)
return
}
}()
go func() {
defer waiter.Done()
if err = NewEncode(w, enc); err != nil {
t.Error(err)
return
}
}()
waiter.Wait()
if err != nil {
return
}
for k, v := range enc {
if d, ok := dec[k]; ok {
if v.Text != d.Text {
t.Errorf("text from decode not exists or mismatch (%q), Encode %q, Decode %q", k, v.Text, d.Text)
return
} else if !bytes.Equal(v.Blob, d.Blob) {
t.Errorf("blob from decode not exists or mismatch (%q), Encode %s, Decode %s", k, hex.EncodeToString(v.Blob), hex.EncodeToString(d.Blob))
return
}
continue
}
t.Errorf("key not exists in decode (%q)", k)
return
}
})
t.Run("Response", func(t *testing.T) {
var err error
var encodeRes, decodeRes proto.Response
encodeRes.BadRequest = true
encodeRes.NotListened = true
encodeRes.SendAuth = true
encodeRes.AgentInfo = &proto.AgentInfo{
Protocol: 1,
UDPPort: 2555,
TCPPort: 3000,
AddrPort: netip.MustParseAddrPort("[::]:10000"),
}
var waiter sync.WaitGroup
waiter.Add(2)
r, w := io.Pipe()
go func() {
defer waiter.Done()
if err = NewDecode(r, &decodeRes); err != nil {
t.Error(err)
return
}
}()
go func() {
defer waiter.Done()
if err = NewEncode(w, encodeRes); err != nil {
t.Error(err)
return
}
}()
waiter.Wait()
if err != nil {
return
} else if decodeRes.BadRequest != encodeRes.BadRequest {
t.Errorf("invalid decode/encode, Current values to BadRequest, Decode %v, Encode %v", decodeRes.BadRequest, encodeRes.BadRequest)
return
} else if decodeRes.NotListened != encodeRes.NotListened {
t.Errorf("invalid decode/encode, Current values to NotListened, Decode %v, Encode %v", decodeRes.NotListened, encodeRes.NotListened)
return
} else if decodeRes.SendAuth != encodeRes.SendAuth {
t.Errorf("invalid decode/encode, Current values to SendAuth, Decode %v, Encode %v", decodeRes.SendAuth, encodeRes.SendAuth)
return
} else if decodeRes.AgentInfo == nil {
t.Errorf("invalid decode, Current values to AgentInfo, Decode %+v, Encode %+v", decodeRes.AgentInfo, encodeRes.AgentInfo)
return
} else if decodeRes.AgentInfo.Protocol != encodeRes.AgentInfo.Protocol {
t.Errorf("invalid decode/encode, Current values to AgentInfo.Protocol, Decode %d, Encode %d", decodeRes.AgentInfo.Protocol, encodeRes.AgentInfo.Protocol)
return
} else if decodeRes.AgentInfo.TCPPort != encodeRes.AgentInfo.TCPPort {
t.Errorf("invalid decode/encode, Current values to AgentInfo.TCPPort, Decode %d, Encode %d", decodeRes.AgentInfo.TCPPort, encodeRes.AgentInfo.TCPPort)
} else if decodeRes.AgentInfo.UDPPort != encodeRes.AgentInfo.UDPPort {
t.Errorf("invalid decode/encode, Current values to AgentInfo.UDPPort, Decode %d, Encode %d", decodeRes.AgentInfo.UDPPort, encodeRes.AgentInfo.UDPPort)
return
} else if decodeRes.AgentInfo.AddrPort.Compare(encodeRes.AgentInfo.AddrPort) != 0 {
t.Errorf("invalid decode/encode, Current values to AgentInfo.AddrPort, Decode %s, Encode %s", decodeRes.AgentInfo.AddrPort, encodeRes.AgentInfo.AddrPort)
return
}
})
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.Ping = new(time.Time)
*encodeRequest.Ping = time.Now()
var waiter sync.WaitGroup
waiter.Add(2)
r, w := io.Pipe()
go func() {
defer waiter.Done()
if err = NewEncode(w, encodeRequest); err != nil {
t.Error(err)
return
}
}()
go func() {
defer waiter.Done()
if err = NewDecode(r, &decodeRequest); err != nil {
t.Error(err)
return
}
}()
waiter.Wait()
if err != nil {
return
} 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))
return
}
})
}

View File

@ -6,10 +6,10 @@ import (
"io"
"log"
"net"
"net/netip"
"sync"
"time"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/pipe"
"sirherobrine23.com.br/Minecraft-Server/go-pproxit/internal/pipe"
)
type writeRoot struct {
@ -25,6 +25,7 @@ type client struct {
fromAgent, toClient net.Conn
bufferCache *bytes.Buffer
bufioCache *bufio.Reader
LastPing time.Time
}
type UDPServer struct {
@ -37,6 +38,29 @@ type UDPServer struct {
rw sync.RWMutex
}
func ListenUDP(network string, laddr *net.UDPAddr) (*UDPServer, error) {
conn, err := net.ListenUDP(network, laddr)
if err != nil {
return nil, err
}
var root = &UDPServer{
rootUdp: conn,
peers: make(map[string]*client),
newPeer: make(chan net.Conn),
peerError: make(chan error),
}
go root.handler()
return root, nil
}
func Listen(Network, address string) (net.Listener, error) {
ip, err := net.ResolveUDPAddr(Network, address)
if err != nil {
return nil, err
}
return ListenUDP(Network, ip)
}
// Local address
func (udpListen *UDPServer) Addr() net.Addr {
return udpListen.rootUdp.LocalAddr()
@ -69,24 +93,30 @@ func (udpListen *UDPServer) Accept() (peer net.Conn, err error) {
}
func (udpListen *UDPServer) handler() {
buff := make([]byte, 32*1024)
for {
buff := make([]byte, 1480)
n, from, err := udpListen.rootUdp.ReadFromUDP(buff)
if err != nil {
return
}
udpListen.rw.Lock()
if nt, exist := udpListen.peers[from.String()]; exist {
if (time.Now().UnixMicro() - nt.LastPing.UnixMicro()) > 100_000_000 {
delete(udpListen.peers, from.String())
}
}
if _, exist := udpListen.peers[from.String()]; !exist {
c := new(client)
c.LastPing = time.Now()
c.bufferCache = new(bytes.Buffer)
c.bufioCache = bufio.NewReader(c.bufferCache)
c.fromAgent, c.toClient = pipe.CreatePipe(from, from)
c.fromAgent, c.toClient = pipe.CreatePipe(from.AddrPort(), from.AddrPort())
udpListen.peers[from.String()] = c
go func(){
go func() {
for _, exist := udpListen.peers[from.String()]; exist; {
io.Copy(c.fromAgent, c.bufioCache)
}
@ -107,30 +137,3 @@ func (udpListen *UDPServer) handler() {
udpListen.rw.RUnlock()
}
}
func listenRoot(network string, laddr *net.UDPAddr) (net.Listener, error) {
conn, err := net.ListenUDP(network, laddr)
if err != nil {
return nil, err
}
var root = &UDPServer{
rootUdp: conn,
peers: make(map[string]*client),
newPeer: make(chan net.Conn),
peerError: make(chan error),
}
go root.handler()
return root, nil
}
func ListenAddrPort(Network string, address netip.AddrPort) (net.Listener, error) {
return listenRoot(Network, net.UDPAddrFromAddrPort(address))
}
func Listen(Network, address string) (net.Listener, error) {
ip, err := net.ResolveUDPAddr(Network, address)
if err != nil {
return nil, err
}
return listenRoot(Network, ip)
}

View File

@ -2,112 +2,42 @@ 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
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
ProtoBoth Protoc = iota // TCP+UDP Protocol
ProtoTCP // TCP Protocol
ProtoUDP // UDP Protocol
)
var (
ErrInvalidBody error = errors.New("invalid body, check request/response")
)
var ErrInvalidBody error = errors.New("invalid body, check request/response")
type Protoc uint8 // Net protocol support
func (pr Protoc) MarshalText() ([]byte, error) { return []byte(pr.String()), nil }
func (pr Protoc) String() string {
switch pr {
case ProtoBoth:
return "TCP + UDP"
case ProtoTCP:
return "TCP"
case ProtoUDP:
return "UDP"
default:
return "Invalid proto"
}
}
type Client struct {
Proto Protoc // Protocol to close (proto.ProtoTCP, proto.ProtoUDP or proto.ProtoBoth)
Client netip.AddrPort // Client address and port
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
}
// Return pointer to value
func Point[T any](val T) *T { return &val }

View File

@ -1,121 +1,16 @@
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
)
var (
ErrProtoBothNoSupported error = errors.New("protocol UDP+TCP not supported currently")
)
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
}
var ErrProtoBothNoSupported error = errors.New("protocol UDP+TCP not supported currently")
// Send request to agent and wait response
type Request struct {
AgentAuth *AgentAuth `json:",omitempty"` // Send agent authentication to controller
AgentAuth []byte `json:",omitempty"` // Send agent authentication to controller
Ping *time.Time `json:",omitempty"` // Send ping time to controller in unix milliseconds
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,198 +1,27 @@
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
)
type AgentInfo struct {
Protocol uint8 // Proto supported (proto.ProtoTCP, proto.ProtoUDP or proto.ProtoBoth)
Protocol Protoc // Proto supported (proto.ProtoTCP, proto.ProtoUDP or proto.ProtoBoth)
UDPPort, TCPPort uint16 // Controller port listened
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
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
}
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
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,90 +0,0 @@
package server
import (
"errors"
"fmt"
"net"
"net/netip"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/udplisterner"
"sirherobrine23.org/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 [36]byte) (TunnelInfo, error)
}
type Server struct {
ControllConn net.Listener
ProcessError chan error
ControlCalls ServerCall
Agents map[string]*Tunnel
}
func NewController(calls ServerCall, local netip.AddrPort) (*Server, error) {
conn, err := udplisterner.ListenAddrPort("udp", local)
if err != nil {
return nil, err
}
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
}
go controller.handlerConn(conn)
}
}
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 {
panic(err)
return
}
if req.AgentAuth == nil {
proto.WriteResponse(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})
return
}
proto.WriteResponse(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,211 +0,0 @@
package server
import (
"io"
"log"
"net"
"net/netip"
"time"
"sirherobrine23.org/Minecraft-Server/go-pproxit/internal/udplisterner"
"sirherobrine23.org/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 uint8) // Register Recived data from client
RegisterTX(client netip.AddrPort, Size int, Proto uint8) // Register Transmitted data from client
}
type TunnelInfo struct {
Proto uint8 // 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 proto.WriteResponse(tun.RootConn, res)
}
type toWr struct {
Proto uint8
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{
Client: proto.Client{
Proto: t.Proto,
Client: t.To,
},
Size: uint64(len(w)),
Data: w[:],
},
})
if err == nil {
return len(w), nil
}
return 0, err
}
func (tun *Tunnel) GetTargetWrite(Proto uint8, 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 {
log.Printf("waiting request from %s", tun.RootConn.RemoteAddr().String())
req, err := proto.ReaderRequest(tun.RootConn)
if err != nil {
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, int(data.Size), 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
}

200
tun.go Normal file
View File

@ -0,0 +1,200 @@
package gopproxit
import (
"io"
"maps"
"net"
"net/netip"
"reflect"
"slices"
"sync"
"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 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 *udplisterner.UDPServer
TCPLocker, UDPLocker sync.Locker
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.TCPLocker, tun.UDPLocker = &sync.Mutex{}, &sync.Mutex{}
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()
}
if tun.TCPServer != nil {
tun.TCPServer.Close()
tun.TCPServer = nil
}
if tun.UDPServer != nil {
tun.UDPServer.Close()
tun.UDPServer = nil
}
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() {
defer tun.Close()
for {
var req proto.Request
if err := structcode.NewDecode(tun.Controller, &req); err != nil {
if err == io.EOF {
return
}
continue
}
go func(req proto.Request) {
if data := *req.ClientClose; req.ClientClose != nil {
switch data.Proto {
case proto.ProtoTCP:
if client, ok := tun.TCPConns[data.Client.String()]; ok {
client.Close()
delete(tun.TCPConns, data.Client.String())
}
case proto.ProtoUDP:
if client, ok := tun.UDPConns[data.Client.String()]; ok {
client.Close()
delete(tun.UDPConns, data.Client.String())
}
}
}
if data := *req.DataTX; req.DataTX != nil {
var conn net.Conn
var ok bool
switch data.Client.Proto {
case proto.ProtoTCP:
if conn, ok = tun.TCPConns[data.Client.Client.String()]; !ok {
return
}
case proto.ProtoUDP:
if conn, ok = tun.UDPConns[data.Client.Client.String()]; !ok {
return
}
}
conn.Write(data.Data)
}
}(req)
}
}
type rx struct {
root net.Conn
proto proto.Protoc
}
func (t rx) Write(p []byte) (int, error) {
err := structcode.NewEncode(t.root, proto.Response{
DataRX: &proto.ClientData{
Data: p,
Client: proto.Client{
Proto: t.proto,
Client: netip.MustParseAddrPort(t.root.RemoteAddr().String()),
},
},
})
if err != nil {
return 0, err
}
return len(p), nil
}
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
}
defer tun.TCPServer.Close()
for {
conn, err := tun.TCPServer.Accept()
if err != nil {
tun.sendErr(err)
return
}
go func() {
tun.TCPLocker.Lock()
tun.TCPConns[conn.RemoteAddr().String()] = conn
tun.TCPLocker.Unlock()
io.Copy(&rx{root: conn, proto: proto.ProtoTCP}, conn)
}()
}
}
func (tun *Tunnel) UDPServerhandler() {
var err error
if tun.UDPServer, err = udplisterner.ListenUDP("udp", net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), tun.Agent.TCPPort))); err != nil {
tun.sendErr(err)
return
}
defer tun.UDPServer.Close()
for {
conn, err := tun.UDPServer.Accept()
if err != nil {
tun.sendErr(err)
return
}
go func() {
tun.UDPLocker.Lock()
tun.UDPConns[conn.RemoteAddr().String()] = conn
tun.UDPLocker.Unlock()
io.Copy(&rx{root: conn, proto: proto.ProtoUDP}, conn)
}()
}
}