Update struct decode and encode #2
15
.github/workflows/test.yaml
vendored
Normal file
15
.github/workflows/test.yaml
vendored
Normal 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 ./...
|
28
README.md
28
README.md
@ -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
235
client.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
221
client/client.go
221
client/client.go
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
},
|
||||
}
|
28
cmd/main.go
28
cmd/main.go
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
},
|
||||
}
|
@ -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
30
docs/proto_pt-br.md
Normal 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
36
go.mod
@ -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
113
go.sum
@ -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=
|
@ -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)
|
||||
}
|
@ -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)
|
||||
|
15
internal/structcode/bytes.go
Normal file
15
internal/structcode/bytes.go
Normal 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)
|
||||
}
|
136
internal/structcode/decode.go
Normal file
136
internal/structcode/decode.go
Normal 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
|
||||
}
|
98
internal/structcode/encode.go
Normal file
98
internal/structcode/encode.go
Normal 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
|
||||
}
|
77
internal/structcode/structcode.go
Normal file
77
internal/structcode/structcode.go
Normal 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())
|
||||
}
|
173
internal/structcode/structcode_test.go
Normal file
173
internal/structcode/structcode_test.go
Normal 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
|
||||
}
|
||||
})
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
116
proto/proto.go
116
proto/proto.go
@ -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 }
|
||||
|
109
proto/request.go
109
proto/request.go
@ -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
|
||||
}
|
||||
|
@ -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
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,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[:]))
|
||||
}
|
211
server/tunnel.go
211
server/tunnel.go
@ -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
200
tun.go
Normal 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)
|
||||
}()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user