WIP: 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