playit.gg go implementation #1

Open
Sirherobrine23 wants to merge 6 commits from breaked into main
24 changed files with 926 additions and 1965 deletions
Showing only changes of commit df6e28b833 - Show all commits

View File

@ -1,7 +1,12 @@
package api
import (
"runtime"
)
const (
GoPlayitVersion string = "0.17.1"
PlayitAPI string = "https://api.playit.gg" // Playit API
TunnelTypeMCBedrock string = "minecraft-bedrock" // Minecraft Bedrock server
TunnelTypeMCJava string = "minecraft-java" // Minecraft java server
@ -26,12 +31,6 @@ const (
)
var (
PlayitAPI string = "https://api.playit.gg" // Playit API
PortType []string = []string{
PortTypeBoth,
PortTypeTcp,
PortTypeUdp,
} // Tunnel protocol supports
TunnelType []string = []string{
TunnelTypeMCBedrock,
TunnelTypeMCJava,
@ -57,3 +56,59 @@ type Api struct {
Code string // Claim code
Secret string // Agent Secret
}
type PortProto string
func (proto PortProto) IsValid() bool {
switch proto {
case PortProto(PortTypeBoth):
case PortProto(PortTypeTcp):
case PortProto(PortTypeUdp): return true
}
return false
}
func (proto PortProto) SetBoth() {
proto = "both"
}
func (proto PortProto) SetTcp() {
proto = "tcp"
}
func (proto PortProto) SetUdp() {
proto = "udp"
}
type Platform string
func (Platform Platform) Host() {
switch runtime.GOOS {
case "linux": Platform = "linux";
case "freebsd": Platform = "freebsd";
case "windows": Platform = "windows";
case "darwin": Platform = "macos";
case "android": Platform = "android";
case "ios": Platform = "ios";
default: Platform = "unknown";
}
}
func (Platform Platform) Linux() {
Platform = "linux"
}
func (Platform Platform) Freebsd() {
Platform = "freebsd"
}
func (Platform Platform) Windows() {
Platform = "windows"
}
func (Platform Platform) Macos() {
Platform = "macos"
}
func (Platform Platform) Android() {
Platform = "android"
}
func (Platform Platform) Ios() {
Platform = "ios"
}
func (Platform Platform) MinecraftPlugin() {
Platform = "minecraft-plugin"
}
func (Platform Platform) Unknown() {
Platform = "unknown"
}

View File

@ -12,7 +12,7 @@ import (
)
var (
ClaimAgents []string = []string{
ClaimAgents []string = []string{
"default", "assignable", "self-managed",
}
)

View File

@ -32,7 +32,7 @@ type AgentTunnel struct {
type AgentPendingTunnel struct {
ID uuid.UUID `json:"id"` // Agent ID
Name string `json:"name"` // Agent Name
PortType string `json:"proto"` // Port type
PortType PortProto `json:"proto"` // Port type
PortCount uint16 `json:"port_count"` // Port count
TunnelType string `json:"tunnel_type"` // Tunnel type
Disabled bool `json:"is_disabled"` // Tunnel is disabled

View File

@ -46,22 +46,22 @@ func (w *Api) requestToApi(Path string, Body io.Reader, Response any, Headers ma
if err = json.NewDecoder(res.Body).Decode(&ResBody); err != nil {
return res, err
}
if res.StatusCode >= 300 {
defer res.Body.Close()
var errStatus struct {
Type string `json:"type"`
Message string `json:"message"`
}
if data, is := ResBody.Data.(string); is {
return res, fmt.Errorf(data)
return res, fmt.Errorf("api.playit.gg: %s", data)
} else if err = recodeJson(&ResBody.Data, &errStatus); err != nil {
return res, err
}
if len(errStatus.Message) > 0 {
return res, fmt.Errorf("%s: %s", errStatus.Type, errStatus.Message)
return res, fmt.Errorf("api.playit.gg: %s %s", errStatus.Type, errStatus.Message)
}
return res, fmt.Errorf("%s", errStatus.Type)
return res, fmt.Errorf("api.playit.gg: %s", errStatus.Type)
}
if Response != nil {
if err = recodeJson(&ResBody.Data, Response); err != nil {

View File

@ -54,23 +54,26 @@ type UseAllocPortAlloc struct {
type UseRegion struct {
Region string `json:"region"`
}
/**
/*
*
"status": "allocated",
"data": {
"assigned_domain": "going-scales.gl.at.ply.gg",
"assigned_srv": null,
"assignment": {
"type": "shared-ip"
},
"id": "f667b538-0294-4817-9332-5cba5e94d79e",
"ip_hostname": "19.ip.gl.ply.gg",
"ip_type": "both",
"port_end": 49913,
"port_start": 49912,
"region": "global",
"static_ip4": "147.185.221.19",
"tunnel_ip": "2602:fbaf:0:1::13"
}
"data": {
"assigned_domain": "going-scales.gl.at.ply.gg",
"assigned_srv": null,
"assignment": {
"type": "shared-ip"
},
"id": "f667b538-0294-4817-9332-5cba5e94d79e",
"ip_hostname": "19.ip.gl.ply.gg",
"ip_type": "both",
"port_end": 49913,
"port_start": 49912,
"region": "global",
"static_ip4": "147.185.221.19",
"tunnel_ip": "2602:fbaf:0:1::13"
}
*/
type TunnelCreateUseAllocation struct {
Status string `json:"status"` // For tunnel list
@ -99,7 +102,7 @@ type Tunnel struct {
ID *uuid.UUID `json:"tunnel_id,omitempty"` // Tunnel UUID
Name string `json:"name,omitempty"` // Tunnel name
TunnelType string `json:"tunnel_type,omitempty"` // Tunnel type from TunnelType const's
PortType string `json:"port_type"` // tcp, udp or both
PortType PortProto `json:"port_type"` // tcp, udp or both
PortCount uint16 `json:"port_count"` // Port count to assign to connect
Origin TunnelOriginCreate `json:"origin"`
Enabled bool `json:"enabled"`
@ -167,14 +170,14 @@ func (w *Api) DeleteTunnel(TunnelID *uuid.UUID) error {
}
type AccountTunnel struct {
ID uuid.UUID `json:"id"`
TunnelType string `json:"tunnel_type"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
PortType string `json:"port_type"`
PortCount int32 `json:"port_count"`
Alloc TunnelCreateUseAllocation `json:"alloc"`
Origin TunnelOriginCreate `json:"origin"`
ID uuid.UUID `json:"id"`
TunnelType string `json:"tunnel_type"`
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
PortType PortProto `json:"port_type"`
PortCount int32 `json:"port_count"`
Alloc TunnelCreateUseAllocation `json:"alloc"`
Origin TunnelOriginCreate `json:"origin"`
Domain *struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`

View File

@ -1,68 +0,0 @@
package tunnel
import (
"net/netip"
"slices"
"sirherobrine23.org/playit-cloud/go-playit/api"
)
type PortType struct {
Value string
}
func (w *PortType) IsValid() bool {
return slices.Contains(api.PortType, w.Value)
}
func (proto *PortProto) SetBoth() {
proto.Value = "both"
}
func (proto *PortProto) SetTcp() {
proto.Value = "tcp"
}
func (proto *PortProto) SetUdp() {
proto.Value = "udp"
}
type AddressValue[T any] struct {
Value T
FromPort, ToPort uint16
}
type AddressLookup[T any] interface {
// Resolve address if exist return value else return nil point
Lookup(IpPort netip.AddrPort, Proto PortType) *AddressValue[T]
}
type MatchIp struct {
IP netip.AddrPort
RegionID *uint16
}
func (mat *MatchIp) Matches(ip netip.AddrPort) bool {
return mat.IP.Compare(ip) == 0
}
type MappingOverride struct {
MatchIP MatchIp
Proto PortType
Port api.PortRange
LocalAddr netip.AddrPort
}
type LookupWithOverrides []MappingOverride
func (Look *LookupWithOverrides) Lookup(IpPort netip.AddrPort, Proto PortType) *AddressValue[netip.AddrPort] {
for _, Over := range *Look {
if Over.Proto.Value == Proto.Value && Over.MatchIP.Matches(IpPort) {
return &AddressValue[netip.AddrPort]{
Value: Over.LocalAddr,
FromPort: Over.Port.From,
ToPort: Over.Port.To,
}
}
}
return &AddressValue[netip.AddrPort]{
Value: netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), IpPort.Port()),
FromPort: IpPort.Port(),
ToPort: IpPort.Port() + 1,
}
}

View File

@ -1,219 +0,0 @@
package tunnel
import (
"encoding/binary"
"fmt"
"io"
"net/netip"
)
type RawSlice struct {
Buff []byte
}
func (w *RawSlice) WriteTo(I io.Writer) error {
_, err := I.Write(w.Buff)
return err
}
func (w *RawSlice) ReadFrom(I io.Reader) error {
_, err := I.Read(w.Buff)
return err
}
func ReadU8(w io.Reader) uint8 {
var value uint8
binary.Read(w, binary.BigEndian, &value)
return value
}
func WriteU8(w io.Writer, value uint8) error {
return binary.Write(w, binary.BigEndian, value)
}
func ReadU16(w io.Reader) uint16 {
var value uint16
binary.Read(w, binary.BigEndian, &value)
return value
}
func WriteU16(w io.Writer, value uint16) error {
return binary.Write(w, binary.BigEndian, value)
}
func ReadU32(w io.Reader) uint32 {
var value uint32
binary.Read(w, binary.BigEndian, &value)
return value
}
func WriteU32(w io.Writer, value uint32) error {
return binary.Write(w, binary.BigEndian, value)
}
func ReadU64(w io.Reader) uint64 {
var value uint64
binary.Read(w, binary.BigEndian, &value)
return value
}
func WriteU64(w io.Writer, value uint64) error {
return binary.Write(w, binary.BigEndian, value)
}
func WriteData(w io.Writer, val any) error {
return binary.Write(w, binary.BigEndian, val)
}
func ReadBuff(w io.Reader, buff []byte) error {
for index := range buff {
if err := binary.Read(w, binary.BigEndian, &buff[index]); err != nil {
return err
}
}
return nil
}
func ReadBuffN(w io.Reader, size int) ([]byte, error) {
buff := make([]byte, size)
return buff, ReadBuff(w, buff)
}
func ReadU8Buff(w io.Reader, buff []uint8) error {
for index := range buff {
if err := binary.Read(w, binary.BigEndian, &buff[index]); err != nil {
return err
}
}
return nil
}
func ReadU16Buff(w io.Reader, buff []uint16) error {
for index := range buff {
if err := binary.Read(w, binary.BigEndian, &buff[index]); err != nil {
return err
}
}
return nil
}
func ReadU32Buff(w io.Reader, buff []uint32) error {
for index := range buff {
if err := binary.Read(w, binary.BigEndian, &buff[index]); err != nil {
return err
}
}
return nil
}
func ReadU64Buff(w io.Reader, buff []uint64) error {
for index := range buff {
if err := binary.Read(w, binary.BigEndian, &buff[index]); err != nil {
return err
}
}
return nil
}
func ReadOption(w io.Reader, callback func(reader io.Reader) error) error {
code := ReadU8(w)
if code == 1 {
return callback(w)
}
return nil
}
func WriteOption(w io.Writer, value MessageEncoding) error {
fmt.Printf("%+v\n", value)
if value != nil {
if err := binary.Write(w, binary.BigEndian, uint8(1)); err != nil {
return err
}
return value.WriteTo(w)
}
return binary.Write(w, binary.BigEndian, uint8(0))
}
func WriteOptionU8(w io.Writer, value *uint8) error {
if value == nil {
return binary.Write(w, binary.BigEndian, uint8(0))
}
if err := binary.Write(w, binary.BigEndian, uint8(1)); err != nil {
return err
}
return WriteU8(w, *value)
}
func WriteOptionU16(w io.Writer, value *uint16) error {
if value == nil {
return binary.Write(w, binary.BigEndian, uint8(0))
}
if err := binary.Write(w, binary.BigEndian, uint8(1)); err != nil {
return err
}
return WriteU16(w, *value)
}
func WriteOptionU32(w io.Writer, value *uint32) error {
if value == nil {
return binary.Write(w, binary.BigEndian, uint8(0))
}
if err := binary.Write(w, binary.BigEndian, uint8(1)); err != nil {
return err
}
return WriteU32(w, *value)
}
func WriteOptionU64(w io.Writer, value *uint64) error {
if value == nil {
return binary.Write(w, binary.BigEndian, uint8(0))
}
if err := binary.Write(w, binary.BigEndian, uint8(1)); err != nil {
return err
}
return WriteU64(w, *value)
}
type AddressPort struct {
netip.AddrPort
}
func (sock *AddressPort) WriteTo(w io.Writer) error {
addr := sock.Addr()
ip, _ := addr.MarshalBinary()
if addr.Is6() {
if err := WriteU8(w, uint8(6)); err != nil {
return err
} else if _, err = w.Write(ip); err != nil {
return err
}
} else {
if err := WriteU8(w, uint8(4)); err != nil {
return err
} else if _, err = w.Write(ip); err != nil {
return err
}
}
if err := WriteU16(w, sock.Port()); err != nil {
return err
}
return nil
}
func (sock *AddressPort) ReadFrom(w io.Reader) error {
switch ReadU8(w) {
case 4:
buff, err := ReadBuffN(w, 4)
if err != nil {
return err
}
sock.AddrPort = netip.AddrPortFrom(netip.AddrFrom4([4]byte(buff)), ReadU16(w))
return nil
case 6:
buff, err := ReadBuffN(w, 16)
if err != nil {
return err
}
sock.AddrPort = netip.AddrPortFrom(netip.AddrFrom16([16]byte(buff)), ReadU16(w))
return nil
}
return fmt.Errorf("cannot get IP type")
}

178
tunnel/binary.go Normal file
View File

@ -0,0 +1,178 @@
package tunnel
import (
"encoding/binary"
"fmt"
"io"
"net/netip"
)
func readU8(r io.Reader) uint8 {
var d uint8
err := binary.Read(r, binary.BigEndian, d)
if err != nil {
panic(err)
}
return d
}
func readU16(r io.Reader) uint16 {
var d uint16
err := binary.Read(r, binary.BigEndian, d)
if err != nil {
panic(err)
}
return d
}
func readU32(r io.Reader) uint32 {
var d uint32
err := binary.Read(r, binary.BigEndian, d)
if err != nil {
panic(err)
}
return d
}
func readU64(r io.Reader) uint64 {
var d uint64
err := binary.Read(r, binary.BigEndian, d)
if err != nil {
panic(err)
}
return d
}
func writeU8(w io.Writer, d uint8) (int64, error) {
return 1, binary.Write(w, binary.BigEndian, d)
}
func writeU16(w io.Writer, d uint16) (int64, error) {
return 2, binary.Write(w, binary.BigEndian, d)
}
func writeU32(w io.Writer, d uint32) (int64, error) {
return 4, binary.Write(w, binary.BigEndian, d)
}
func writeU64(w io.Writer, d uint64) (int64, error) {
return 8, binary.Write(w, binary.BigEndian, d)
}
func readByteN(r io.Reader, size int) (buff []byte, err error) {
buff = make([]byte, size)
for index := range buff {
if err = binary.Read(r, binary.BigEndian, &buff[index]); err != nil {
buff = buff[:index]
return
}
}
return
}
func writeBytes(w io.Writer, buff []byte) (n int64, err error) {
n = int64(len(buff))
err = binary.Write(w, binary.BigEndian, buff)
return
}
func addrWrite(w io.Writer, addr netip.Addr) (n int64, err error) {
if addr.Is6() {
if _, err = writeU8(w, 6); err != nil {
n = 0
return
} else if _, err = writeBytes(w, addr.AsSlice()); err != nil {
n = 1
return
}
n = 17
return
}
if _, err = writeU8(w, 4); err != nil {
n = 0
return
} else if _, err = writeBytes(w, addr.AsSlice()); err != nil {
n = 1
return
}
n = 5
return
}
func addrRead(r io.Reader) (addr netip.Addr, n int64, err error) {
var buff []byte
n = 1
switch readU8(r) {
case 4:
buff, err = readByteN(r, 4)
n += int64(len(buff))
if err != nil {
return
}
addr = netip.AddrFrom4([4]byte(buff))
return
case 6:
buff, err = readByteN(r, 16)
n += int64(len(buff))
if err != nil {
return
}
netip.AddrFrom16([16]byte(buff))
return
}
err = fmt.Errorf("connet get ip type")
return
}
func addrPortRead(r io.Reader) (netip.AddrPort, int64, error) {
switch readU8(r) {
case 4:
buff, err := readByteN(r, 4)
if err != nil {
return netip.AddrPort{}, int64(len(buff)), err
}
return netip.AddrPortFrom(netip.AddrFrom4([4]byte(buff)), readU16(r)), 6, nil
case 6:
buff, err := readByteN(r, 16)
if err != nil {
return netip.AddrPort{}, int64(len(buff)), err
}
return netip.AddrPortFrom(netip.AddrFrom16([16]byte(buff)), readU16(r)), 19, nil
}
return netip.AddrPort{}, 1, fmt.Errorf("connet get ip type")
}
func addrPortWrite(w io.Writer, addr netip.AddrPort) (n int64, err error) {
if !addr.IsValid() {
return 0, fmt.Errorf("invalid ip address")
} else if addr.Addr().Is6() {
if _, err = writeU8(w, 6); err != nil {
return 0, err
} else if err = binary.Write(w, binary.BigEndian, addr.Addr().AsSlice()); err != nil {
return 1, err
}
n = 18
return
}
if _, err = writeU8(w, 4); err != nil {
return 0, err
} else if err = binary.Write(w, binary.BigEndian, addr.Addr().AsSlice()); err != nil {
return 1, err
}
n = 5
return
}
func writeOption(w io.Writer, d any, callback func(w io.Writer) (n int64, err error)) (n int64, err error) {
if d == nil {
return writeU8(w, 0)
}
n, err = writeU8(w, 1)
if err != nil {
return
}
n2, err2 := callback(w)
return n + n2, err2
}
func readOption(r io.Reader, callback func(r io.Reader) (n int64, err error)) (n int64, err error) {
n = 1
switch readU8(r) {
case 0:
return 1, nil
case 1:
n2, err := callback(r)
return n + n2, err
}
return 1, fmt.Errorf("invalid Option value")
}

View File

@ -1,141 +0,0 @@
package tunnel
import (
"bytes"
"fmt"
"net/netip"
"time"
"sirherobrine23.org/playit-cloud/go-playit/api"
)
type AuthenticatedControl struct {
ApiClient api.Api
Conn ConnectedControl
CurrentPing *uint32
LastPong Pong
ForceEpired bool
Registered AgentRegistered
Buff []byte
}
func (Auth *AuthenticatedControl) Send(Req ControlRpcMessage[MessageEncoding]) error {
Auth.Buff = []byte{}
bufio := bytes.NewBuffer(Auth.Buff)
if err := Req.WriteTo(bufio); err != nil {
return err
}
Auth.Buff = bufio.Bytes()
_, err := Auth.Conn.Udp.WriteToUDPAddrPort(Auth.Buff, Auth.Conn.ControlAddr)
if err != nil {
return err
}
return nil
}
func (Auth *AuthenticatedControl) SendKeepAlive(RequestID uint64) error {
return Auth.Send(ControlRpcMessage[MessageEncoding]{
RequestID: RequestID,
Content: &ControlRequest{
AgentKeepAlive: &Auth.Registered.ID,
},
})
}
func (Auth *AuthenticatedControl) SendSetupUDPChannel(RequestID uint64) error {
return Auth.Send(ControlRpcMessage[MessageEncoding]{
RequestID: RequestID,
Content: &ControlRequest{
SetupUdpChannel: &Auth.Registered.ID,
},
})
}
func (Auth *AuthenticatedControl) SendPing(RequestID uint64, Now time.Time) error {
return Auth.Send(ControlRpcMessage[MessageEncoding]{
RequestID: RequestID,
Content: &ControlRequest{
Ping: &Ping{
Now: Now,
CurrentPing: Auth.CurrentPing,
SessionID: &Auth.Registered.ID,
},
},
})
}
func (Auth *AuthenticatedControl) FlowChanged() bool {
return Auth.LastPong.ClientAddr.Compare(Auth.LastPong.ClientAddr.AddrPort) != 0
}
func (Auth *AuthenticatedControl) IsIspired() bool {
return Auth.ForceEpired || Auth.LastPong.SessionExpireAt == nil || Auth.FlowChanged()
}
func (Auth *AuthenticatedControl) IntoRequiresAuth() *ConnectedControl {
return &ConnectedControl{
ControlAddr: Auth.Conn.ControlAddr,
Udp: Auth.Conn.Udp,
Pong: &Auth.LastPong,
}
}
type InvalidRemote struct {
Expected, Got netip.AddrPort
}
func (a InvalidRemote) Error() string {
return fmt.Sprintf("expected %s, got %s", a.Expected.String(), a.Got.String())
}
func (Auth *AuthenticatedControl) RecFeedMsg() (*ControlFeed, error) {
Auth.Buff = append(Auth.Buff, make([]byte, 1024)...)
size, remote, err := Auth.Conn.Udp.ReadFromUDP(Auth.Buff)
LogDebug.Println(size, remote, err)
if err != nil {
return nil, err
} else if remote.AddrPort().Compare(Auth.Conn.ControlAddr) != 0 {
return nil, InvalidRemote{Expected: Auth.Conn.ControlAddr, Got: remote.AddrPort()}
}
var feed ControlFeed
if err := feed.ReadFrom(bytes.NewBuffer(Auth.Buff[size:])); err != nil {
return nil, err
}
if feed.Response != nil {
if feed.Response.Content != nil {
if feed.Response.Content.AgentRegistered != nil {
LogDebug.Println("agent registred")
LogDebug.Printf("%+v\n", feed.Response.Content.AgentRegistered)
Auth.Registered = *feed.Response.Content.AgentRegistered
} else if feed.Response.Content.Pong != nil {
CurrentPing := uint32(feed.Response.Content.Pong.RequestNow - uint64(time.Now().UnixMilli()))
Auth.CurrentPing = &CurrentPing
Auth.LastPong = *feed.Response.Content.Pong
if feed.Response.Content.Pong.SessionExpireAt != nil {
Auth.Registered.ExpiresAt = time.UnixMilli(int64(*feed.Response.Content.Pong.SessionExpireAt))
}
}
}
}
return &feed, nil
}
func (Auth *AuthenticatedControl) Authenticate() error {
conn, err := (&ConnectedControl{
ControlAddr: Auth.Conn.ControlAddr,
Udp: Auth.Conn.Udp,
Pong: &Auth.LastPong,
}).Authenticate(Auth.ApiClient)
if err != nil {
return err
}
Auth.Buff = conn.Buff
Auth.Conn = conn.Conn
Auth.CurrentPing = conn.CurrentPing
Auth.LastPong = conn.LastPong
Auth.Registered = conn.Registered
return nil
}

View File

@ -1,110 +1,140 @@
package tunnel
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"net/netip"
)
type ClaimInstructions struct {
Address AddressPort
Token []byte
}
func (w *ClaimInstructions) WriteTo(I io.Writer) error {
if err := w.Address.WriteTo(I); err != nil {
return err
} else if err := WriteU64(I, uint64(len(w.Token))); err != nil {
return err
} else if err = binary.Write(I, binary.BigEndian, w.Token); err != nil {
return err
}
return nil
}
func (w *ClaimInstructions) ReadFrom(I io.Reader) error {
w.Address = AddressPort{}
if err := w.Address.ReadFrom(I); err != nil {
return err
}
w.Token = make([]byte, ReadU64(I))
if err := ReadBuff(I, w.Token); err != nil {
return err
}
return nil
}
type NewClient struct {
ConnectAddr AddressPort
PeerAddr AddressPort
ClaimInstructions ClaimInstructions
TunnelServerId uint64
DataCenterId uint32
}
func (w *NewClient) WriteTo(I io.Writer) error {
if err := w.ConnectAddr.WriteTo(I); err != nil {
return err
} else if w.PeerAddr.WriteTo(I); err != nil {
return err
} else if w.ClaimInstructions.WriteTo(I); err != nil {
return err
} else if err := WriteU64(I, w.TunnelServerId); err != nil {
return err
} else if err := WriteU32(I, w.DataCenterId); err != nil {
return err
}
return nil
}
func (w *NewClient) ReadFrom(I io.Reader) error {
w.ConnectAddr, w.PeerAddr = AddressPort{}, AddressPort{}
if err := w.ConnectAddr.ReadFrom(I); err != nil {
return err
} else if err := w.PeerAddr.ReadFrom(I); err != nil {
return err
} else if err := w.ClaimInstructions.ReadFrom(I); err != nil {
return err
}
w.TunnelServerId, w.DataCenterId = ReadU64(I), ReadU32(I)
return nil
}
var (
ErrFeedRead error = fmt.Errorf("invalid controlFeed id")
)
type ControlFeed struct {
Response *ControlRpcMessage[*ControlResponse]
NewClient *NewClient
}
func (w *ControlFeed) WriteTo(I io.Writer) error {
defer func(){
d, _ := json.MarshalIndent(w, "", " ")
LogDebug.Printf("Write Feed: %s\n", string(d))
}()
if w.Response != nil {
if err := WriteU32(I, 1); err != nil {
return err
}
return w.Response.WriteTo(I)
} else if w.NewClient != nil {
if err := WriteU32(I, 2); err != nil {
return err
}
return w.NewClient.WriteTo(I)
func (Feed *ControlFeed) ReadFrom(r io.Reader) (n int64, err error) {
id := readU32(r)
if id == 1 {
Feed.Response = new(ControlRpcMessage[*ControlResponse])
Feed.Response.Content = new(ControlResponse)
n, err = Feed.Response.ReadFrom(r)
n += 4
return
} else if id == 2 {
Feed.NewClient = &NewClient{}
n, err = Feed.NewClient.ReadFrom(r)
n += 4
return
}
return fmt.Errorf("set ResponseControl or NewClient")
return 4, ErrFeedRead
}
func (w *ControlFeed) ReadFrom(I io.Reader) error {
defer func(){
d, _ := json.MarshalIndent(w, "", " ")
LogDebug.Printf("Read Feed: %s\n", string(d))
}()
switch ReadU32(I) {
case 1:
w.Response = &ControlRpcMessage[*ControlResponse]{}
w.Response.Content = &ControlResponse{}
return w.Response.ReadFrom(I)
case 2:
w.NewClient = &NewClient{}
return w.NewClient.ReadFrom(I)
func (Feed *ControlFeed) WriteTo(w io.Writer) (n int64, err error) {
if Feed.Response != nil {
if err := writeU32(w, 1); err != nil {
return 0, err
}
n, err = Feed.Response.WriteTo(w)
n += 4
return
} else if Feed.NewClient != nil {
if err := writeU32(w, 2); err != nil {
return 0, err
}
n, err = Feed.NewClient.WriteTo(w)
n += 4
return
}
return fmt.Errorf("invalid ControlFeed id")
return 0, fmt.Errorf("")
}
type NewClient struct {
ConnectAddr netip.AddrPort
PeerAddr netip.AddrPort
ClaimInstructions ClaimInstructions
TunnelServerId uint64
DataCenterId uint32
}
func (client *NewClient) ReadFrom(r io.Reader) (n int64, err error) {
client.ConnectAddr, n, err = addrPortRead(r)
if err != nil {
return n, err
}
n2 := n
client.PeerAddr, n, err = addrPortRead(r)
if err != nil {
return n2 + n, err
}
n3 := n2 + n
n, err = client.ClaimInstructions.ReadFrom(r);
if err != nil {
return n3 + n, err
}
n+=n3 + 8 + 4
client.TunnelServerId, client.DataCenterId = readU64(r), readU32(r)
return
}
func (client *NewClient) WriteTo(w io.Writer) (n int64, err error) {
n, err = addrPortWrite(w, client.ConnectAddr)
if err != nil {
return n, err
}
n2 := n
n, err = addrPortWrite(w, client.PeerAddr)
if err != nil {
return n+n2, err
}
n3:= n+n2
if n, err = client.ClaimInstructions.WriteTo(w); err != nil {
return n + n3, err
}
n4 := n + n3
if err = writeU64(w, client.TunnelServerId); err != nil {
return n4, err
}
n4 += 8
if err = writeU32(w, client.DataCenterId); err != nil {
return n4, err
}
n = n4+8
return
}
type ClaimInstructions struct {
Address netip.AddrPort
Token []byte
}
func (claim *ClaimInstructions) ReadFrom(r io.Reader) (n int64, err error) {
claim.Address, n, err = addrPortRead(r)
if err != nil {
return n, err
}
claim.Token, err = readByteN(r, int(readU64(r)))
n += int64(len(claim.Token)) + 8
return
}
func (claim *ClaimInstructions) WriteTo(w io.Writer) (n int64, err error) {
n, err = addrPortWrite(w, claim.Address)
if err != nil {
return n, err
}
if err = writeU64(w, uint64(len(claim.Token))); err != nil {
return n, err
}
n2 := 8 + n
n, err = writeBytes(w, claim.Token)
if err != nil {
return n2, err
}
n = n2 + n
return
}

View File

@ -1,411 +0,0 @@
package tunnel
import (
"encoding/binary"
"fmt"
"io"
"time"
)
type Ping struct {
Now time.Time
CurrentPing *uint32
SessionID *AgentSessionId
}
func (w *Ping) WriteTo(I io.Writer) error {
if err := WriteU64(I, uint64(w.Now.UnixMilli())); err != nil {
return err
}
if w.CurrentPing == nil {
if err := binary.Write(I, binary.BigEndian, uint8(0)); err != nil {
return err
}
} else {
if err := binary.Write(I, binary.BigEndian, uint8(1)); err != nil {
return err
} else if err := binary.Write(I, binary.BigEndian, w.CurrentPing); err != nil {
return err
}
}
if w.SessionID == nil {
return binary.Write(I, binary.BigEndian, uint8(0))
} else if err := binary.Write(I, binary.BigEndian, uint8(1)); err != nil {
return err
}
return w.SessionID.WriteTo(I)
}
func (w *Ping) ReadFrom(I io.Reader) error {
w.Now = time.UnixMilli(int64(ReadU64(I)))
CurrentPing := ReadU32(I)
w.CurrentPing = &CurrentPing
w.SessionID = &AgentSessionId{}
w.SessionID.ReadFrom(I)
return nil
}
type Pong struct {
RequestNow uint64
ServerNow uint64
ServerId uint64
DataCenterId uint32
ClientAddr AddressPort
TunnelAddr AddressPort
SessionExpireAt *uint64
}
func (w *Pong) WriteTo(I io.Writer) error {
if err := WriteU64(I, w.RequestNow); err != nil {
return err
} else if err := WriteU64(I, w.ServerNow); err != nil {
return err
} else if err := WriteU64(I, w.ServerId); err != nil {
return err
} else if err := WriteU32(I, w.DataCenterId); err != nil {
return err
} else if err := w.ClientAddr.WriteTo(I); err != nil {
return err
} else if err := w.TunnelAddr.WriteTo(I); err != nil {
return err
} else if err := WriteOptionU64(I, w.SessionExpireAt); err != nil {
return err
}
return nil
}
func (w *Pong) ReadFrom(I io.Reader) error {
w.RequestNow, w.ServerNow, w.ServerId = ReadU64(I), ReadU64(I), ReadU64(I)
w.DataCenterId = ReadU32(I)
w.ClientAddr = AddressPort{}
w.TunnelAddr = AddressPort{}
if err := w.ClientAddr.ReadFrom(I); err != nil {
return err
} else if err := w.TunnelAddr.ReadFrom(I); err != nil {
return err
}
Sess := ReadU64(I)
w.SessionExpireAt = &Sess
return nil
}
type AgentRegister struct {
AccountID, AgentId, AgentVersion, Timestamp uint64
ClientAddr, TunnelAddr AddressPort
Signature []byte // 32 bytes
}
func (w *AgentRegister) WritePlain(buff io.Writer) error {
if err := WriteU64(buff, w.AccountID); err != nil {
return err
} else if err := WriteU64(buff, w.AgentId); err != nil {
return err
} else if err := WriteU64(buff, w.AgentVersion); err != nil {
return err
} else if err := WriteU64(buff, w.Timestamp); err != nil {
return err
} else if err := w.ClientAddr.WriteTo(buff); err != nil {
return err
} else if err := w.TunnelAddr.WriteTo(buff); err != nil {
return err
}
return nil
}
func (w *AgentRegister) WriteTo(I io.Writer) error {
if err := WriteU64(I, w.AccountID); err != nil {
return err
} else if err := WriteU64(I, w.AgentId); err != nil {
return err
} else if err := WriteU64(I, w.AgentVersion); err != nil {
return err
} else if err := WriteU64(I, w.Timestamp); err != nil {
return err
} else if err := w.ClientAddr.WriteTo(I); err != nil {
return err
} else if err := w.TunnelAddr.WriteTo(I); err != nil {
return err
} else if err := binary.Write(I, binary.BigEndian, w.Signature); err != nil {
return err
}
return nil
}
func (w *AgentRegister) ReadFrom(I io.Reader) error {
w.AccountID = ReadU64(I)
w.AgentId = ReadU64(I)
w.AgentVersion = ReadU64(I)
w.Timestamp = ReadU64(I)
w.ClientAddr, w.TunnelAddr = AddressPort{}, AddressPort{}
if err := w.ClientAddr.ReadFrom(I); err != nil {
return err
} else if err := w.TunnelAddr.ReadFrom(I); err != nil {
return err
}
w.Signature = make([]byte, 32)
if err := ReadBuff(I, w.Signature); err != nil {
return err
}
return nil
}
type AgentCheckPortMapping struct {
AgentSessionId AgentSessionId
PortRange PortRange
}
func (w *AgentCheckPortMapping) WriteTo(I io.Writer) error {
if err := w.AgentSessionId.WriteTo(I); err != nil {
return err
} else if err := w.PortRange.WriteTo(I); err != nil {
return err
}
return nil
}
func (w *AgentCheckPortMapping) ReadFrom(I io.Reader) error {
w.AgentSessionId, w.PortRange = AgentSessionId{}, PortRange{}
if err := w.AgentSessionId.ReadFrom(I); err != nil {
return err
} else if err := w.PortRange.ReadFrom(I); err != nil {
return err
}
return nil
}
type ControlRequest struct {
Ping *Ping
AgentRegister *AgentRegister
AgentKeepAlive *AgentSessionId
SetupUdpChannel *AgentSessionId
AgentCheckPortMapping *AgentCheckPortMapping
}
func (w *ControlRequest) WriteTo(I io.Writer) error {
if w.Ping != nil {
if err := WriteU32(I, uint32(6)); err != nil {
return err
}
return w.Ping.WriteTo(I)
} else if w.AgentRegister != nil {
if err := WriteU32(I, uint32(2)); err != nil {
return err
}
return w.AgentRegister.WriteTo(I)
} else if w.AgentKeepAlive != nil {
if err := WriteU32(I, uint32(3)); err != nil {
return err
}
return w.AgentKeepAlive.WriteTo(I)
} else if w.SetupUdpChannel != nil {
if err := WriteU32(I, uint32(4)); err != nil {
return err
}
return w.SetupUdpChannel.WriteTo(I)
} else if w.AgentCheckPortMapping != nil {
if err := WriteU32(I, uint32(5)); err != nil {
return err
}
return w.AgentCheckPortMapping.WriteTo(I)
}
return fmt.Errorf("set ControlRequest")
}
func (w *ControlRequest) ReadFrom(I io.Reader) error {
switch ReadU32(I) {
case 1:
w.Ping = &Ping{}
return w.Ping.ReadFrom(I)
case 2:
w.AgentRegister = &AgentRegister{}
return w.AgentRegister.ReadFrom(I)
case 3:
w.AgentKeepAlive = &AgentSessionId{}
return w.AgentKeepAlive.ReadFrom(I)
case 4:
w.SetupUdpChannel = &AgentSessionId{}
return w.SetupUdpChannel.ReadFrom(I)
case 5:
w.AgentCheckPortMapping = &AgentCheckPortMapping{}
return w.AgentCheckPortMapping.ReadFrom(I)
}
return fmt.Errorf("invalid ControlRequest id")
}
type AgentRegistered struct {
ID AgentSessionId
ExpiresAt time.Time
}
func (w *AgentRegistered) WriteTo(I io.Writer) error {
if err := w.ID.WriteTo(I); err != nil {
return err
} else if err := WriteU64(I, uint64(w.ExpiresAt.UnixMilli())); err != nil {
return err
}
return nil
}
func (w *AgentRegistered) ReadFrom(I io.Reader) error {
w.ID = AgentSessionId{}
if err := w.ID.ReadFrom(I); err != nil {
return err
}
w.ExpiresAt = time.UnixMilli(int64(ReadU64(I)))
return nil
}
type AgentPortMappingFound struct {
ToAgent *AgentSessionId
}
func (agentPort *AgentPortMappingFound) WriteTo(I io.Writer) error {
if agentPort.ToAgent != nil {
if err := WriteU32(I, 1); err != nil {
return err
} else if err := agentPort.ToAgent.WriteTo(I); err != nil {
return err
}
}
return nil
}
func (agentPort *AgentPortMappingFound) ReadFrom(I io.Reader) error {
if ReadU32(I) == 1 {
agentPort.ToAgent = &AgentSessionId{}
return agentPort.ToAgent.ReadFrom(I)
}
return fmt.Errorf("unknown AgentPortMappingFound id")
}
type AgentPortMapping struct {
Range PortRange
Found *AgentPortMappingFound
}
func (w *AgentPortMapping) WriteTo(I io.Writer) error {
if err := w.Range.WriteTo(I); err != nil {
return err
} else if err := w.Found.WriteTo(I); err != nil {
return err
}
return nil
}
func (w *AgentPortMapping) ReadFrom(I io.Reader) error {
if err := w.Range.ReadFrom(I); err != nil {
return err
} else if err := w.Found.ReadFrom(I); err != nil {
return err
}
return nil
}
type UdpChannelDetails struct {
TunnelAddr AddressPort
Token []byte
}
func (w *UdpChannelDetails) WriteTo(I io.Writer) error {
if err := w.TunnelAddr.WriteTo(I); err != nil {
return err
} else if err := WriteU64(I, uint64(len(w.Token))); err != nil {
return err
} else if err := binary.Write(I, binary.BigEndian, w.Token); err != nil {
return err
}
return nil
}
func (w *UdpChannelDetails) ReadFrom(I io.Reader) error {
w.TunnelAddr = AddressPort{}
if err := w.TunnelAddr.ReadFrom(I); err != nil {
return err
}
w.Token = make([]byte, ReadU64(I))
if err := ReadBuff(I, w.Token); err != nil {
return err
}
return nil
}
type ControlResponse struct {
InvalidSignature bool
Unauthorized bool
RequestQueued bool
TryAgainLater bool
Pong *Pong
AgentRegistered *AgentRegistered
AgentPortMapping *AgentPortMapping
UdpChannelDetails *UdpChannelDetails
}
func (w *ControlResponse) WriteTo(I io.Writer) error {
if w.Pong != nil {
if err := WriteU32(I, 1); err != nil {
return err
}
return w.Pong.WriteTo(I)
} else if w.InvalidSignature {
if err := WriteU32(I, 2); err != nil {
return err
}
return nil
} else if w.Unauthorized {
if err := WriteU32(I, 3); err != nil {
return err
}
return nil
} else if w.RequestQueued {
if err := WriteU32(I, 4); err != nil {
return err
}
return nil
} else if w.TryAgainLater {
if err := WriteU32(I, 5); err != nil {
return err
}
return nil
} else if w.AgentRegistered != nil {
if err := WriteU32(I, 6); err != nil {
return err
}
return w.AgentRegistered.WriteTo(I)
} else if w.AgentPortMapping != nil {
if err := WriteU32(I, 7); err != nil {
return err
}
return w.AgentPortMapping.WriteTo(I)
} else if w.UdpChannelDetails != nil {
if err := WriteU32(I, 8); err != nil {
return err
}
return w.UdpChannelDetails.WriteTo(I)
}
return fmt.Errorf("set one option to write")
}
func (w *ControlResponse) ReadFrom(I io.Reader) error {
switch ReadU32(I) {
case 1:
w.Pong = &Pong{}
return w.Pong.ReadFrom(I)
case 2:
w.InvalidSignature = true
return nil
case 3:
w.Unauthorized = true
return nil
case 4:
w.RequestQueued = true
return nil
case 5:
w.TryAgainLater = true
return nil
case 6:
w.AgentRegistered = &AgentRegistered{}
return w.AgentRegistered.ReadFrom(I)
case 7:
w.AgentPortMapping = &AgentPortMapping{}
return w.AgentPortMapping.ReadFrom(I)
case 8:
w.UdpChannelDetails = &UdpChannelDetails{}
return w.UdpChannelDetails.ReadFrom(I)
}
return fmt.Errorf("invalid ControlResponse id")
}

394
tunnel/control_messages.go Normal file
View File

@ -0,0 +1,394 @@
package tunnel
import (
"bytes"
"fmt"
"io"
"net/netip"
"time"
)
type ControlRequest struct {
Ping *Ping
AgentRegister *AgentRegister
AgentKeepAlive *AgentSessionId
SetupUdpChannel *AgentSessionId
AgentCheckPortMapping *AgentCheckPortMapping
}
func (Control *ControlRequest) WriteTo(w io.Writer) (n int64, err error) {
if Control.Ping != nil {
n, err = writeU32(w, 6)
if err != nil {
return
}
n2 := n
n, err = Control.Ping.WriteTo(w)
if err != nil {
return n2, err
}
n += n2
return
} else if Control.AgentRegister != nil {
n, err = writeU32(w, 2)
if err != nil {
return
}
n2 := n
n, err = Control.AgentRegister.WriteTo(w)
if err != nil {
return n2, err
}
n += n2
return
} else if Control.AgentKeepAlive != nil {
n, err = writeU32(w, 3)
if err != nil {
return
}
n2 := n
n, err = Control.AgentKeepAlive.WriteTo(w)
if err != nil {
return n2, err
}
n += n2
return
} else if Control.SetupUdpChannel != nil {
n, err = writeU32(w, 4)
if err != nil {
return
}
n2 := n
n, err = Control.SetupUdpChannel.WriteTo(w)
if err != nil {
return n2, err
}
n += n2
return
} else if Control.AgentCheckPortMapping != nil {
n, err = writeU32(w, 5)
if err != nil {
return
}
n2 := n
n, err = Control.AgentCheckPortMapping.WriteTo(w)
if err != nil {
return n2, err
}
n += n2
return
}
err = fmt.Errorf("set ControlRequest")
return
}
func (Control *ControlRequest) ReadFrom(r io.Reader) (n int64, err error) {
n = 1
switch readU32(r) {
case 1:
Control.Ping = new(Ping)
np, err := Control.Ping.ReadFrom(r)
return np + n, err
case 2:
Control.AgentRegister = new(AgentRegister)
np, err := Control.AgentRegister.ReadFrom(r)
return np + n, err
case 3:
Control.AgentKeepAlive = new(AgentSessionId)
np, err := Control.AgentKeepAlive.ReadFrom(r)
return np + n, err
case 4:
Control.SetupUdpChannel = new(AgentSessionId)
np, err := Control.SetupUdpChannel.ReadFrom(r)
return np + n, err
case 5:
Control.AgentCheckPortMapping = new(AgentCheckPortMapping)
np, err := Control.AgentCheckPortMapping.ReadFrom(r)
return np + n, err
}
err = fmt.Errorf("invalid ControlRequest id")
return
}
type AgentCheckPortMapping struct {
AgentSessionId AgentSessionId
PortRange PortRange
}
func (Agent *AgentCheckPortMapping) WriteTo(w io.Writer) (n int64, err error) {
n, err = Agent.AgentSessionId.WriteTo(w)
if err != nil {
return
}
n2 := n
n, err = Agent.PortRange.WriteTo(w)
return n + n2, err
}
func (Agent *AgentCheckPortMapping) ReadFrom(r io.Reader) (n int64, err error) {
n, err = Agent.AgentSessionId.ReadFrom(r)
if err != nil {
return
}
n2 := n
n, err = Agent.AgentSessionId.ReadFrom(r)
return n + n2, err
}
type Ping struct {
Now time.Time
CurrentPing *time.Time
SessionId *AgentSessionId
}
func (ping *Ping) WriteTo(w io.Writer) (n int64, err error) {
n, err = writeU64(w, uint64(ping.Now.UnixMilli()))
if err != nil {
return
}
n2 := n
if n, err = writeOption(w, ping.CurrentPing, func(w io.Writer) (int64, error) {
return writeU64(w, uint64(ping.CurrentPing.UnixMilli()))
}); err != nil {
n = n2
return
}
n += n2
if n, err = writeOption(w, ping.SessionId, ping.SessionId.WriteTo); err != nil {
n = n2
return
}
return
}
func (ping *Ping) ReadFrom(r io.Reader) (n int64, err error) {
ping.Now = time.UnixMilli(int64(readU64(r)))
n, err = readOption(r, func(r io.Reader) (n int64, err error) {
ping.CurrentPing = new(time.Time)
d, _ := time.UnixMilli(int64(readU64(r))).MarshalBinary()
ping.CurrentPing.UnmarshalBinary(d)
return 8, nil
})
n += 8
if err != nil {
return
}
n, err = readOption(r, func(r io.Reader) (n int64, err error) {
return ping.SessionId.ReadFrom(r)
})
if err != nil {
return
}
return
}
type AgentRegister struct {
AccountID, AgentId, AgentVersion, Timestamp uint64
ClientAddr, TunnelAddr netip.AddrPort
Signature [32]byte
}
func (agent *AgentRegister) writePlain() *bytes.Buffer {
buff := new(bytes.Buffer)
writeU64(buff, agent.AccountID)
writeU64(buff, agent.AgentId)
writeU64(buff, agent.AgentVersion)
writeU64(buff, agent.Timestamp)
addrPortWrite(buff, agent.ClientAddr)
addrPortWrite(buff, agent.TunnelAddr)
return buff
}
func (agent *AgentRegister) UpdateSignature(hmac HmacSha256) {
agent.Signature = hmac.Sign(agent.writePlain().Bytes())
}
func (agent *AgentRegister) VerifySignature(hmac HmacSha256) bool {
return hmac.Verify(agent.writePlain().Bytes(), agent.Signature[:])
}
func (AgentReg *AgentRegister) WriteTo(w io.Writer) (n int64, err error) {
if _, err := writeU64(w, AgentReg.AccountID); err != nil {
return 0, err
} else if _, err := writeU64(w, AgentReg.AgentId); err != nil {
return 0, err
} else if _, err := writeU64(w, AgentReg.AgentVersion); err != nil {
return 0, err
} else if _, err := writeU64(w, AgentReg.Timestamp); err != nil {
return 0, err
}
n = 8 * 4
if n2, err := addrPortWrite(w, AgentReg.ClientAddr); err != nil {
return n, err
} else if n3, err := addrPortWrite(w, AgentReg.TunnelAddr); err != nil {
return n + n2, err
} else {
n += n3
}
if n4, err := w.Write(AgentReg.Signature[:]); err != nil {
return n, err
} else {
n += int64(n4)
}
return
}
func (AgentReg *AgentRegister) ReadFrom(r io.Reader) (n int64, err error) {
AgentReg.AccountID, AgentReg.AccountID, AgentReg.AgentVersion, AgentReg.Timestamp = readU64(r), readU64(r), readU64(r), readU64(r)
if AgentReg.ClientAddr, n, err = addrPortRead(r); err != nil {
return
} else if AgentReg.TunnelAddr, n, err = addrPortRead(r); err != nil {
return
}
AgentReg.Signature = [32]byte(make([]byte, 32))
if n2, _ := r.Read(AgentReg.Signature[:]); n != 32 {
return int64(n2), fmt.Errorf("missing signature")
}
return
}
type ControlResponse struct {
InvalidSignature, Unauthorized, RequestQueued, TryAgainLater bool
Pong *Pong
AgentRegistered *AgentRegistered
AgentPortMapping *AgentPortMapping
UdpChannelDetails *UdpChannelDetails
}
func (Control *ControlResponse) WriteTo(w io.Writer) (n int64, err error) {
defer func() {
if err == nil {
n += 4
}
}()
if Control.Pong != nil {
writeU32(w, 1)
n, err = Control.Pong.WriteTo(w)
return
} else if Control.InvalidSignature {
return writeU32(w, 2)
} else if Control.Unauthorized {
return writeU32(w, 3)
} else if Control.RequestQueued {
return writeU32(w, 4)
} else if Control.TryAgainLater {
return writeU32(w, 5)
} else if Control.AgentRegistered != nil {
writeU32(w, 6)
return Control.AgentRegistered.WriteTo(w)
} else if Control.AgentPortMapping != nil {
writeU32(w, 7)
return Control.AgentPortMapping.WriteTo(w)
} else if Control.UdpChannelDetails != nil {
writeU32(w, 8)
return Control.UdpChannelDetails.WriteTo(w)
} else {
err = fmt.Errorf("insert any options to write")
}
return
}
func (Control *ControlResponse) ReadFrom(r io.Reader) (n int64, err error) {
defer func() {
if err == nil {
n += 4
}
}()
switch readU32(r) {
case 1:
Control.Pong = &Pong{}
return Control.Pong.ReadFrom(r)
case 2:
Control.InvalidSignature = true
return
case 3:
Control.Unauthorized = true
return
case 4:
Control.RequestQueued = true
return
case 5:
Control.TryAgainLater = true
return
case 6:
Control.AgentRegistered = &AgentRegistered{}
return Control.AgentRegistered.ReadFrom(r)
case 7:
Control.AgentPortMapping = &AgentPortMapping{}
return Control.AgentPortMapping.ReadFrom(r)
case 8:
Control.UdpChannelDetails = &UdpChannelDetails{}
return Control.UdpChannelDetails.ReadFrom(r)
default:
err = fmt.Errorf("invalid ControlResponse id")
}
return
}
type AgentPortMapping struct {
Range PortRange
Found *AgentPortMappingFound
}
func (Agent *AgentPortMapping) WriteTo(w io.Writer) (n int64, err error) {
Agent.Range.WriteTo(w)
Agent.Found.WriteTo(w)
return
}
func (Agent *AgentPortMapping) ReadFrom(r io.Reader) (n int64, err error) {
Agent.Range.ReadFrom(r)
Agent.Found.ReadFrom(r)
return
}
type AgentPortMappingFound struct {
ToAgent *AgentSessionId
}
func (Agent *AgentPortMappingFound) WriteTo(w io.Writer) (n int64, err error) {
if Agent.ToAgent != nil {
writeU32(w, 1)
Agent.ToAgent.WriteTo(w)
return
}
return
}
func (Agent *AgentPortMappingFound) ReadFrom(r io.Reader) (n int64, err error) {
if readU32(r) == 1 {
defer func() { n += 4 }()
Agent.ToAgent = new(AgentSessionId)
return Agent.ToAgent.ReadFrom(r)
}
return 4, fmt.Errorf("unknown AgentPortMappingFound id")
}
type UdpChannelDetails struct {
TunnelAddr netip.AddrPort
Token []byte
}
func (UdpChannel *UdpChannelDetails) WriteTo(w io.Writer) (n int64, err error) {
addrPortWrite(w, UdpChannel.TunnelAddr)
writeU64(w, uint64(len(UdpChannel.Token)))
writeBytes(w, UdpChannel.Token)
return
}
func (UdpChannel *UdpChannelDetails) ReadFrom(r io.Reader) (n int64, err error) {
UdpChannel.TunnelAddr, _, _ = addrPortRead(r)
UdpChannel.Token, _ = readByteN(r, int(readU64(r)))
return
}
type Pong struct {
RequestNow, ServerNow time.Time
ServerId uint64
DataCenterId uint32
ClientAddr, TunnelAddr netip.AddrPort
SessionExpireAt *time.Time
}
func (pong *Pong) WriteTo(w io.Writer) (n int64, err error) {
}
func (pong *Pong) ReadFrom(r io.Reader) (n int64, err error) {}
type AgentRegistered struct {
Id AgentSessionId
ExpiresAt time.Time
}
func (agent *AgentRegistered) WriteTo(w io.Writer) (n int64, err error) {}
func (agent *AgentRegistered) ReadFrom(r io.Reader) (n int64, err error) {}

29
tunnel/hmacsha256.go Normal file
View File

@ -0,0 +1,29 @@
package tunnel
import (
"crypto/hmac"
"crypto/sha256"
"hash"
)
type HmacSha256 struct {
mac hash.Hash
}
func NewHmacSha256(secret []byte) *HmacSha256 {
mac := hmac.New(sha256.New, secret)
return &HmacSha256{mac}
}
func (h *HmacSha256) Verify(data, sig []byte) bool {
expectedMAC := h.mac.Sum(nil)
h.mac.Reset()
h.mac.Write(data)
return hmac.Equal(expectedMAC, sig)
}
func (h *HmacSha256) Sign(data []byte) [32]byte {
h.mac.Reset()
h.mac.Write(data)
return [32]byte(h.mac.Sum(nil))
}

View File

@ -47,15 +47,15 @@ func TcpSocket(SpecialLan bool, Peer, Host netip.AddrPort) (*net.TCPConn, error)
local_ip := mapToLocalIP4(Peer.Addr().AsSlice());
stream, err := net.DialTCP("tcp4", net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte(local_ip.To4())), 0)), net.TCPAddrFromAddrPort(Host))
if err != nil {
LogDebug.Printf("Failed to establish connection using special lan %s for flow %s -> %s\n", local_ip, Peer.String(), Host.String())
logDebug.Printf("Failed to establish connection using special lan %s for flow %s -> %s\n", local_ip, Peer.String(), Host.String())
return nil, err
}
return stream, nil
}
LogDebug.Printf("Failed to bind connection to special local address to support IP based banning")
logDebug.Printf("Failed to bind connection to special local address to support IP based banning")
stream, err := net.DialTCP("tcp", nil, net.TCPAddrFromAddrPort(Host))
if err != nil {
LogDebug.Printf("Failed to establish connection for flow %s -> %s. Is your server running? %q", Peer.String(), Host.String(), err.Error())
logDebug.Printf("Failed to establish connection for flow %s -> %s. Is your server running? %q", Peer.String(), Host.String(), err.Error())
return nil, err
}
return stream, nil
@ -68,14 +68,15 @@ func UdpSocket(SpecialLan bool, Peer, Host netip.AddrPort) (*net.UDPConn, error)
local_port := 40000 + (Peer.Port() % 24000);
stream, err := net.DialUDP("udp4", net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom16([16]byte(local_ip)), local_port)), net.UDPAddrFromAddrPort(Host))
if err != nil {
LogDebug.Printf("Failed to bind UDP port to %d to have connections survive agent restart: %s", local_port, err.Error())
logDebug.Printf("Failed to bind UDP port to %d to have connections survive agent restart: %s", local_port, err.Error())
stream, err = net.DialUDP("udp4", net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom16([16]byte(local_ip)), 0)), net.UDPAddrFromAddrPort(Host))
if err != nil {
err2 := err
stream, err = net.DialUDP("udp4", nil, nil)
if err != nil {
return nil, err
}
LogDebug.Printf("Failed to bind UDP to special local address, in-game ip banning will not work: %s", err.Error())
logDebug.Printf("Failed to bind UDP to special local address, in-game ip banning will not work: %s", err2.Error())
}
}
return stream, nil

View File

@ -1,85 +1,97 @@
package tunnel
import (
"encoding/binary"
"fmt"
"io"
"net"
"net/netip"
)
type PortProto struct {
// 1 => "tcp"
//
// 2 => "udp"
//
// 3 => "both"
Value string
}
func (w *PortProto) WriteTo(I io.Writer) error {
switch w.Value {
case "tcp": return WriteU8(I, 1)
case "udp": return WriteU8(I, 2)
case "both": return WriteU8(I, 3)
}
return fmt.Errorf("set valid proto")
}
func (w *PortProto) ReadFrom(I io.Reader) error {
switch ReadU8(I) {
case 1:
w.Value = "tcp"
return nil
case 2:
w.Value = "udp"
return nil
case 3:
w.Value = "both"
return nil
}
return fmt.Errorf("invalid proto")
}
type AgentSessionId struct {
SessionID, AccountID, AgentID uint64
}
func (w *AgentSessionId) WriteTo(I io.Writer) error {
var err error
if err = WriteU64(I, w.SessionID); err != nil {
return err
} else if err = WriteU64(I, w.AccountID); err != nil {
return err
} else if err = WriteU64(I, w.AgentID); err != nil {
return err
}
return nil
}
func (w *AgentSessionId) ReadFrom(I io.Reader) error {
w.SessionID, w.AccountID, w.AgentID = ReadU64(I), ReadU64(I), ReadU64(I)
return nil
}
type PortRange struct {
IP net.IP
PortStart uint16
PortEnd uint16
PortProto PortProto
IP netip.Addr
PortStart, PortEnd uint16
PortProto PortProto
}
func (w *PortRange) WriteTo(I io.Writer) error {
if err := binary.Write(I, binary.BigEndian, w.IP); err != nil {
return err
} else if err := WriteU16(I, w.PortStart); err != nil {
return err
} else if err := WriteU16(I, w.PortEnd); err != nil {
return err
} else if err := w.PortProto.WriteTo(I); err != nil {
return err
type PortProto string
func (AgentSession *AgentSessionId) WriteTo(w io.Writer) (int64, error) {
if _, err := writeU64(w, AgentSession.SessionID); err != nil {
return 0, err
} else if _, err = writeU64(w, AgentSession.AccountID); err != nil {
return 8, err
} else if _, err = writeU64(w, AgentSession.AgentID); err != nil {
return 16, err
}
return nil
return 24, nil
}
func (AgentSession *AgentSessionId) ReadFrom(r io.Reader) (int64, error) {
AgentSession.SessionID, AgentSession.AccountID, AgentSession.AgentID = readU64(r), readU64(r), readU64(r)
return 24, nil
}
func (w *PortRange) ReadFrom(I io.Reader) error {
return nil
}
func (portRange *PortRange) WriteTo(w io.Writer) (int64, error) {
var len int64 = 4
sizeIP, err := addrWrite(w, portRange.IP)
if err != nil {
return len, err
}
len += sizeIP
if _, err = writeU16(w, portRange.PortStart); err != nil {
return len, err
} else if _, err = writeU16(w, portRange.PortEnd); err != nil {
return len, err
}
protoSize, err := portRange.PortProto.WriteTo(w)
if err != nil {
return len, err
}
return len + protoSize, nil
}
func (portRange *PortRange) ReadFrom(r io.Reader) (int64, error) {
var (
ipSize int64
err error
)
portRange.IP, ipSize, err = addrRead(r)
if err != nil {
return ipSize, err
}
ipSize += 4
portRange.PortStart, portRange.PortEnd = readU16(r), readU16(r)
portRange.PortProto = PortProto("")
protoSize, err := portRange.PortProto.ReadFrom(r)
if err != nil {
return ipSize, err
}
return ipSize + protoSize, nil
}
func (proto PortProto) WriteTo(w io.Writer) (int64, error) {
switch proto {
case "tcp":
return writeU8(w, 1)
case "udp":
return writeU8(w, 2)
case "both":
return writeU8(w, 3)
}
return 0, fmt.Errorf("invalid port proto")
}
func (proto PortProto) ReadFrom(r io.Reader) (int64, error) {
switch readU8(r) {
case 1:
proto = PortProto("tcp")
case 2:
proto = PortProto("udp")
case 3:
proto = PortProto("both")
default: return 0, fmt.Errorf("invalid port proto")
}
return 1, nil
}

View File

@ -1,13 +1,13 @@
package tunnel
import (
"encoding/json"
"encoding/binary"
"io"
)
type MessageEncoding interface {
WriteTo(I io.Writer) error
ReadFrom(I io.Reader) error
io.ReaderFrom
io.WriterTo
}
type ControlRpcMessage[T MessageEncoding] struct {
@ -15,22 +15,22 @@ type ControlRpcMessage[T MessageEncoding] struct {
Content T // Convert with .(*type)
}
func (w *ControlRpcMessage[T]) WriteTo(I io.Writer) error {
if err := WriteU64(I, w.RequestID); err != nil {
return err
func (rpc *ControlRpcMessage[T]) WriteTo(w io.Writer) (n int64, err error) {
if err = binary.Write(w, binary.BigEndian, rpc.RequestID); err != nil {
return 0, err
} else if n, err = rpc.Content.WriteTo(w); err != nil {
return 8, err
}
defer func() {
d, _ := json.MarshalIndent(w, "", " ")
LogDebug.Printf("Write RPC: %s\n", string(d))
}()
return w.Content.WriteTo(I)
n += 8
return
}
func (w *ControlRpcMessage[T]) ReadFrom(I io.Reader) error {
w.RequestID = ReadU64(I)
defer func() {
d, _ := json.MarshalIndent(w, "", " ")
LogDebug.Printf("Read RPC: %s\n", string(d))
}()
return w.Content.ReadFrom(I)
func (rpc *ControlRpcMessage[T]) ReadFrom(r io.Reader) (n int64, err error) {
if err = binary.Read(r, binary.BigEndian, &rpc.RequestID); err != nil {
n = 0
return n, err
} else if n, err = rpc.Content.ReadFrom(r); err != nil {
return 8, err
}
n += 8
return
}

View File

@ -1,94 +0,0 @@
package tunnel
import (
"net/netip"
"os"
"os/signal"
"sync/atomic"
"time"
)
type TunnelRunner struct {
Lookup AddressLookup[netip.AddrPort]
Tunnel SimplesTunnel
KeepRunning atomic.Bool
}
func (tun *TunnelRunner) UseSpecialLan(set bool) {
panic("no implemented UseSpecialLan")
}
func (tun *TunnelRunner) Run() error {
channel := make(chan error)
// udp := tun.Tunnel.UdpTunnel
// Setup Tunnel
if err := tun.Tunnel.Setup(); err != nil {
return err
}
LogDebug.Println("Success Tunnel setup")
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
LogDebug.Println("Cleaning process")
tun.KeepRunning.Store(false)
tun.Tunnel.ControlChannel.Conn.Udp.Close()
os.Exit(1)
}()
// TCP Clients
go func() {
lastControlUpdate := time.Now().UnixMilli()
defer os.Exit(2)
for tun.KeepRunning.Load() {
now := time.Now().UnixMilli()
if 30_000 < now-lastControlUpdate {
lastControlUpdate = now
LogDebug.Println("Reloading control addr")
if _, err := tun.Tunnel.ReloadControlAddr(); err != nil {
LogDebug.Println("failed to reload control addr")
LogDebug.Println(err)
return
}
}
newClient, err := tun.Tunnel.Update()
if err != nil {
LogDebug.Println(err.Error())
channel <- err
return
} else if newClient == nil {
continue
}
LogDebug.Println("tcp client")
LogDebug.Printf("%#v\n", newClient)
LogDebug.Panic(newClient)
}
}()
// go func() {
// buffer := make([]byte, 2048)
// had_success := false
// for tun.KeepRunning.Load() {
// LogDebug.Println("rec udp tun")
// rx, err := udp.ReceiveFrom(buffer)
// if err != nil {
// LogDebug.Println(err)
// if had_success {
// LogDebug.Panicln("got error")
// }
// time.Sleep(time.Second)
// continue
// }
// LogDebug.Println("success")
// had_success = true
// if rx.ConfirmerdConnection {
// continue
// }
// LogDebug.Printf("%#v\n", rx.ReceivedPacket)
// }
// }()
return <-channel
}

View File

@ -1,22 +0,0 @@
package rwlock
import "sync"
type Rwlock[T any] struct {
Value T
sync.RWMutex
}
// Get writer value and return unlocker function
//
// if call this function before end call function
func (rw *Rwlock[T]) Write() (T, func()) {
rw.Lock()
return rw.Value, rw.Unlock
}
// Get reader value and unlocker function
func (rw *Rwlock[T]) Read() (T, func()) {
rw.RLock()
return rw.Value, rw.RUnlock
}

View File

@ -1,190 +0,0 @@
package tunnel
import (
"bytes"
"encoding/hex"
"fmt"
"net"
"net/netip"
"time"
"sirherobrine23.org/playit-cloud/go-playit/api"
)
type SetupFindSuitableChannel struct {
Address []netip.AddrPort
}
func (Setup *SetupFindSuitableChannel) Setup() (*ConnectedControl, error) {
for _, Addr := range Setup.Address {
network := "udp6"
if Addr.Addr().Is4() && !Addr.Addr().Is4In6() {
network = "udp4"
}
conn, err := net.ListenUDP(network, nil)
if err != nil {
fmt.Println(err.Error())
continue
}
for range 3 {
// Make initial ping
buffer := bytes.NewBuffer([]byte{})
if err = (&ControlRpcMessage[*ControlRequest]{
RequestID: 1,
Content: &ControlRequest{
Ping: &Ping{
Now: time.Now(),
CurrentPing: nil,
SessionID: nil,
},
},
}).WriteTo(buffer); err != nil {
conn.Close()
return nil, err
}
// Write initial ping
_, err = conn.WriteToUDP(buffer.Bytes(), net.UDPAddrFromAddrPort(Addr))
if err != nil {
conn.Close()
break
}
for range 5 {
buff := make([]byte, 2048)
if err = conn.SetReadDeadline(time.Now().Add(time.Millisecond * 5)); err != nil {
return nil, err
}
bytesSize, peer, err := conn.ReadFrom(buff)
if err != nil {
if netErr, isNet := err.(net.Error); isNet {
if netErr.Timeout() {
continue
}
}
return nil, err
} else if peer.String() != Addr.String() {
continue
}
buff = buff[:bytesSize]
var feed ControlFeed
if err := feed.ReadFrom(bytes.NewReader(buff)); err != nil {
return nil, err
} else if feed.Response == nil {
return nil, fmt.Errorf("unexpected control feed")
}
msg := feed.Response
if msg.RequestID != 1 {
continue
} else if msg.Content.Pong == nil {
return nil, fmt.Errorf("expected pong got other response")
}
return &ConnectedControl{
ControlAddr: Addr,
Udp: conn,
Pong: msg.Content.Pong,
}, nil
}
}
}
return nil, fmt.Errorf("cannot make UDP tunnel to playit controller, check you internet conenction")
}
type ConnectedControl struct {
ControlAddr netip.AddrPort
Udp *net.UDPConn
Pong *Pong
}
func (Control *ConnectedControl) Authenticate(Api api.Api) (*AuthenticatedControl, error) {
if !Control.Pong.ClientAddr.AddrPort.IsValid() {
return nil, fmt.Errorf("invalid pong Client address")
} else if !Control.Pong.TunnelAddr.AddrPort.IsValid() {
return nil, fmt.Errorf("invalid pong Tunnel address")
}
LogDebug.Println("Registring agent proto")
tk, err := Api.ProtoRegisterRegister(Control.Pong.ClientAddr.AddrPort, Control.Pong.TunnelAddr.AddrPort)
if err != nil {
LogDebug.Println("failed to sign and register")
return nil, err
}
tkBytes, err := hex.DecodeString(tk)
if err != nil {
return nil, err
}
for range 5 {
buffer := bytes.NewBuffer([]byte{})
if err := (&ControlRpcMessage[*RawSlice]{
RequestID: 10,
Content: &RawSlice{
Buff: tkBytes,
},
}).WriteTo(buffer); err != nil {
return nil, err
}
_, err := Control.Udp.WriteTo(buffer.Bytes(), net.UDPAddrFromAddrPort(Control.ControlAddr))
if err != nil {
return nil, err
}
for range 5 {
reciver := append(buffer.Bytes(), make([]byte, 1024)...)
Control.Udp.SetReadDeadline(time.Now().Add(time.Millisecond * 5))
recSize, remote, err := Control.Udp.ReadFrom(reciver)
if err != nil {
if errNet, isNet := err.(net.Error); isNet {
if errNet.Timeout() {
LogDebug.Println("Timeout")
break
}
}
return nil, err
} else if remote.String() != Control.ControlAddr.String() {
LogDebug.Println("got response not from tunnel server")
continue
}
feed := &ControlFeed{}
if err = feed.ReadFrom(bytes.NewReader(reciver[:recSize])); err != nil {
LogDebug.Println("failed to read response from tunnel")
return nil, err
} else if feed.Response.RequestID != 10 {
LogDebug.Println("got response for different request")
continue
} else if feed.Response == nil || feed.Response.Content == nil {
LogDebug.Println("feed response or Response content is empty")
return nil, fmt.Errorf("cannot get response")
}
controlRes := feed.Response.Content
if controlRes.RequestQueued {
LogDebug.Println("register queued, waiting 1s")
time.Sleep(time.Second)
continue
} else if controlRes.InvalidSignature {
return nil, fmt.Errorf("register return invalid signature")
} else if controlRes.Unauthorized {
return nil, fmt.Errorf("unauthorized")
} else if controlRes.AgentRegistered != nil {
return &AuthenticatedControl{
ApiClient: Api,
Conn: *Control,
LastPong: *Control.Pong,
CurrentPing: nil,
Registered: *controlRes.AgentRegistered,
Buff: reciver[recSize:],
ForceEpired: false,
}, nil
}
LogDebug.Println("expected AgentRegistered but got something else")
}
}
return nil, fmt.Errorf("failed1 to connect agent")
}

View File

@ -1,213 +0,0 @@
package tunnel
import (
"encoding/json"
"fmt"
"net/netip"
"slices"
"time"
"sirherobrine23.org/playit-cloud/go-playit/api"
)
type SimplesTunnel struct {
ApiClaim api.Api
ControlAddr netip.AddrPort
ControlChannel *AuthenticatedControl
UdpTunnel UdpTunnel
LastKeepAlive uint64
LastPing uint64
LastPong uint64
LastUdpAuth uint64
lastControlTargets []netip.AddrPort
}
func ControlAddresses(Api api.Api) ([]netip.AddrPort, error) {
controls, err := Api.AgentRoutings(nil)
if err != nil {
return nil, err
}
addrs := []netip.AddrPort{}
for _, v := range append(controls.Targets6, controls.Targets4...) {
addrs = append(addrs, netip.AddrPortFrom(v, 5525))
}
return addrs, nil
}
func (Tun *SimplesTunnel) Setup() error {
if err := AssignUdpTunnel(&Tun.UdpTunnel); err != nil {
return err
}
addresses, err := ControlAddresses(Tun.ApiClaim)
if err != nil {
return err
}
setup, err := (&SetupFindSuitableChannel{Address: addresses}).Setup()
if err != nil {
return err
}
control_channel, err := setup.Authenticate(Tun.ApiClaim)
if err != nil {
return err
}
Tun.ControlAddr = setup.ControlAddr
Tun.ControlChannel = control_channel
return nil
}
func (Tun *SimplesTunnel) ReloadControlAddr() (bool, error) {
addresses, err := ControlAddresses(Tun.ApiClaim)
if err != nil {
return false, err
}
if slices.ContainsFunc(Tun.lastControlTargets, func(a netip.AddrPort) bool {
return !slices.ContainsFunc(addresses, func(b netip.AddrPort) bool {
return a.Compare(b) == 0
})
}) {
return false, nil
}
setup, err := (&SetupFindSuitableChannel{addresses}).Setup()
if err != nil {
return false, err
}
updated, err := Tun.UpdateControlAddr(*setup)
Tun.lastControlTargets = addresses
return updated, err
}
func (Tun *SimplesTunnel) UpdateControlAddr(conncted ConnectedControl) (ok bool, err error) {
if conncted.ControlAddr.Compare(Tun.ControlAddr) == 0 {
LogDebug.Println("not required Update control addr")
return
}
var controlChannel *AuthenticatedControl
controlChannel, err = conncted.Authenticate(Tun.ApiClaim)
if err != nil {
return
}
LogDebug.Printf("Update control address %s to %s\n", Tun.ControlAddr.String(), conncted.ControlAddr.String())
Tun.ControlChannel = controlChannel
Tun.ControlAddr = conncted.ControlAddr
Tun.LastPing = 0
Tun.LastKeepAlive = 0
Tun.LastUdpAuth = 0
Tun.UdpTunnel.InvalidateSession()
ok = true
return
}
func (Tun *SimplesTunnel) Update() (*NewClient, error) {
if Tun.ControlChannel.IsIspired() {
LogDebug.Println("Creating new controller channel...")
if err := Tun.ControlChannel.Authenticate(); err != nil {
LogDebug.Println(err)
time.Sleep(time.Second * 2)
return nil, nil
}
}
now := uint64(time.Now().UnixMilli())
if now-Tun.LastPing > 1_000 {
Tun.LastPing = now
if err := Tun.ControlChannel.SendPing(200, time.UnixMilli(int64(now))); err != nil {
LogDebug.Println("failed to send ping")
}
}
// d, _ := json.MarshalIndent(Tun, "", " ")
// LogDebug.Panicf(string(d))
if Tun.UdpTunnel.RequiresAuth() {
if 5_000 < now-Tun.LastUdpAuth {
Tun.LastUdpAuth = now
if err := Tun.ControlChannel.SendSetupUDPChannel(9000); err != nil {
LogDebug.Println("failed to send udp setup request to control")
LogDebug.Println(err)
}
}
} else if Tun.UdpTunnel.RequireResend() {
if 1_000 < now-Tun.LastUdpAuth {
Tun.LastUdpAuth = now
if _, err := Tun.UdpTunnel.ResendToken(); err != nil {
LogDebug.Println("failed to send udp auth request")
LogDebug.Println(err)
}
}
timeTillExpire := func(x, y uint64) uint64 {
if x > y {
return y
}
return x
}(uint64(Tun.ControlChannel.Registered.ExpiresAt.UnixMilli()), uint64(now))
if 10_000 < now-Tun.LastKeepAlive && timeTillExpire < 30_000 {
Tun.LastKeepAlive = now
LogDebug.Println("send KeepAlive")
if err := Tun.ControlChannel.SendKeepAlive(100); err != nil {
LogDebug.Println("failed to send KeepAlive")
LogDebug.Println(err)
}
if err := Tun.ControlChannel.SendSetupUDPChannel(1); err != nil {
LogDebug.Println("failed to send setup udp channel request")
LogDebug.Println(err)
}
}
timeout := 0
for range 30 {
if timeout >= 10 {
LogDebug.Println("feed recv timeout")
break
}
LogDebug.Println("RX Feed message")
men, err := Tun.ControlChannel.RecFeedMsg()
if err != nil {
timeout++
LogDebug.Printf("failed to parse response: %s\n", err.Error())
continue
}
if men.NewClient != nil {
return men.NewClient, nil
} else if men.Response != nil {
cont := men.Response.Content
if cont.UdpChannelDetails != nil {
LogDebug.Print("Response SetUdpTunnel")
if err := Tun.UdpTunnel.SetUdpTunnel(*men.Response.Content.UdpChannelDetails); err != nil {
timeout++
LogDebug.Print(err)
}
} else if cont.Pong != nil {
Tun.LastPong = uint64(time.Now().UnixMilli())
if cont.Pong.ClientAddr.Compare(Tun.ControlChannel.Conn.Pong.ClientAddr.AddrPort) != 0 {
LogDebug.Printf("Client IP changed: %q -> %q\n", cont.Pong.ClientAddr, Tun.ControlChannel.Conn.Pong.ClientAddr.AddrPort)
}
} else if cont.Unauthorized {
LogDebug.Panicln("unauthorized, check token or reload agent")
Tun.ControlChannel.ForceEpired = true
return nil, fmt.Errorf("unauthorized, check token or reload agent")
} else {
LogDebug.Printf("got response")
d , _ := json.MarshalIndent(men, "", " ")
LogDebug.Printf(string(d))
}
}
}
}
if Tun.LastPong != 0 && uint64(time.Now().UnixMilli())-Tun.LastPong > 6_000 {
LogDebug.Println("timeout waiting for pong")
Tun.LastPong = 0
Tun.ControlChannel.ForceEpired = true
}
return nil, nil
}

View File

@ -1,25 +0,0 @@
package tunnel
import "net"
type TcpTunnel struct {
ClaimInstructions ClaimInstructions
}
func (tcp *TcpTunnel) Connect() (*net.TCPConn, error) {
stream, err := net.DialTCP("tcp", nil, net.TCPAddrFromAddrPort(tcp.ClaimInstructions.Address.AddrPort))
if err != nil {
LogDebug.Printf("%q: Failed to establish connection to tunnel server\n", tcp.ClaimInstructions.Address.AddrPort.String())
return nil, err
}
if _, err := stream.Write(tcp.ClaimInstructions.Token); err != nil {
stream.Close()
return nil, err
}
res := make([]byte, 8)
if _, err := stream.Read(res); err != nil {
stream.Close()
return nil, err
}
LogDebug.Printf("%+v\n", res)
return stream, nil
}

View File

@ -5,4 +5,5 @@ import (
"os"
)
var LogDebug = log.New(os.Stderr, "go-playit.gg: ", log.Ldate|log.Ltime)
// Write log and show in terminal to debug
var logDebug *log.Logger = log.New(os.Stderr, "plait.gg", log.Ltime|log.Ldate)

View File

@ -1,134 +0,0 @@
package tunnel
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net/netip"
)
const (
REDIRECT_FLOW_4_FOOTER_ID_OLD uint64 = 0x5cb867cf788173b2
REDIRECT_FLOW_4_FOOTER_ID uint64 = 0x4448474f48414344
REDIRECT_FLOW_6_FOOTER_ID uint64 = 0x6668676f68616366
UDP_CHANNEL_ESTABLISH_ID uint64 = 0xd01fe6830ddce781
V4_LEN int = 20
V6_LEN int = 48
)
type UdpFlowBase struct {
Src, Dst netip.AddrPort
}
type UdpFlow struct {
V4 *UdpFlowBase
V6 *struct {
UdpFlowBase
Flow uint32
}
}
func (w *UdpFlow) Len() int {
if w.V4 == nil {
return V6_LEN
}
return V4_LEN
}
func (w *UdpFlow) Src() netip.AddrPort {
if w.V4 == nil {
return w.V6.UdpFlowBase.Src
}
return w.V4.Src
}
func (w *UdpFlow) Dst() netip.AddrPort {
if w.V4 == nil {
return w.V6.UdpFlowBase.Dst
}
return w.V4.Dst
}
func (w *UdpFlow) WriteTo(writer io.Writer) error {
var conn UdpFlowBase
if w.V4 != nil {
conn = *w.V4
} else {
conn = w.V6.UdpFlowBase
}
if err := WriteData(writer, conn.Src.Addr().AsSlice()); err != nil {
return err
} else if err := WriteData(writer, conn.Dst.Addr().AsSlice()); err != nil {
return err
} else if err := WriteU16(writer, conn.Src.Port()); err != nil {
return err
} else if err := WriteU16(writer, conn.Dst.Port()); err != nil {
return err
}
if w.V4 != nil {
if err := WriteU64(writer, REDIRECT_FLOW_4_FOOTER_ID_OLD); err != nil {
return err
}
} else {
if err := WriteU32(writer, w.V6.Flow); err != nil {
return err
} else if err := WriteU64(writer, REDIRECT_FLOW_6_FOOTER_ID); err != nil {
return err
}
}
return nil
}
func FromTailUdpFlow(slice []byte) (*UdpFlow, uint64, error) {
if len(slice) < 8 {
return nil, 0, fmt.Errorf("not space to footer")
}
footer := binary.BigEndian.Uint64(slice[len(slice)-8:])
switch footer {
case REDIRECT_FLOW_4_FOOTER_ID | REDIRECT_FLOW_4_FOOTER_ID_OLD:
if len(slice) < V4_LEN {
return nil, 0, fmt.Errorf("v4 not have space")
}
slice = slice[len(slice)-V4_LEN:]
src_ip, _ := ReadBuffN(bytes.NewReader(slice), 4)
srcIP, _ := netip.AddrFromSlice(src_ip)
dst_ip, _ := ReadBuffN(bytes.NewReader(slice), 4)
dstIP, _ := netip.AddrFromSlice(dst_ip)
src_port, dst_port := ReadU16(bytes.NewReader(slice)), ReadU16(bytes.NewReader(slice))
return &UdpFlow{
V4: &UdpFlowBase{
Src: netip.AddrPortFrom(srcIP, src_port),
Dst: netip.AddrPortFrom(dstIP, dst_port),
},
}, 0, nil
case REDIRECT_FLOW_6_FOOTER_ID:
if len(slice) < V6_LEN {
return nil, footer, fmt.Errorf("v6 not have space")
}
slice = slice[len(slice)-V6_LEN:]
src_ip, _ := ReadBuffN(bytes.NewReader(slice), 16)
srcIP, _ := netip.AddrFromSlice(src_ip)
dst_ip, _ := ReadBuffN(bytes.NewReader(slice), 16)
dstIP, _ := netip.AddrFromSlice(dst_ip)
src_port, dst_port := ReadU16(bytes.NewReader(slice)), ReadU16(bytes.NewReader(slice))
flow := ReadU32(bytes.NewReader(slice))
return &UdpFlow{
V6: &struct {
UdpFlowBase
Flow uint32
}{
UdpFlowBase{
Src: netip.AddrPortFrom(srcIP, src_port),
Dst: netip.AddrPortFrom(dstIP, dst_port),
},
flow,
},
}, 0, nil
}
return nil, footer, nil
}

View File

@ -1,225 +0,0 @@
package tunnel
import (
"bytes"
"encoding/hex"
"fmt"
"net"
"net/netip"
"slices"
"sync/atomic"
"time"
"sirherobrine23.org/playit-cloud/go-playit/tunnel/rwlock"
)
type UdpTunnel struct {
Udp4 *net.UDPConn
Udp6 *net.UDPConn
Details rwlock.Rwlock[ChannelDetails]
LastConfirm atomic.Uint32
LastSend atomic.Uint32
}
type ChannelDetails struct {
Udp *UdpChannelDetails
AddrHistory []netip.AddrPort
}
func AssignUdpTunnel(tunUdp *UdpTunnel) error {
LogDebug.Println("Assign UDP Tunnel IPv4")
udp4, err := net.ListenUDP("udp4", nil)
if err != nil {
return err
}
tunUdp.Udp4 = udp4
// IPv6 opcional
LogDebug.Println("Assign UDP Tunnel IPv6")
if tunUdp.Udp6, err = net.ListenUDP("udp6", nil); err != nil {
LogDebug.Println("Cannot listen IPv6 Udp Tunnel")
tunUdp.Udp6 = nil
err = nil
}
tunUdp.Details = rwlock.Rwlock[ChannelDetails]{Value: ChannelDetails{
AddrHistory: []netip.AddrPort{},
Udp: nil,
}}
tunUdp.LastConfirm = atomic.Uint32{}
tunUdp.LastSend = atomic.Uint32{}
tunUdp.LastConfirm.Store(0)
tunUdp.LastSend.Store(0)
return nil
}
func (udp *UdpTunnel) IsSetup() bool {
data, unlock := udp.Details.Read()
defer unlock()
return data.Udp != nil
}
func (udp *UdpTunnel) InvalidateSession() {
udp.LastConfirm.Store(0)
udp.LastSend.Store(0)
}
func now_sec() uint32 {
return uint32(time.Now().UnixMilli()) / 1_000
}
func (udp *UdpTunnel) RequireResend() bool {
last_confirm := udp.LastConfirm.Load()
/* send token every 10 seconds */
return 10 < now_sec()-last_confirm
}
func (udp *UdpTunnel) RequiresAuth() bool {
lastConf, lastSend := udp.LastConfirm.Load(), udp.LastSend.Load()
if lastSend < lastConf {
return false
}
return 5 < now_sec()-lastSend
}
func (udp *UdpTunnel) SetUdpTunnel(details UdpChannelDetails) error {
LogDebug.Println("Updating Udp Tunnel")
lock, unlock := udp.Details.Write()
if lock.Udp != nil {
current := lock.Udp
if bytes.Equal(current.Token, details.Token) && current.TunnelAddr.Compare(details.TunnelAddr.AddrPort) == 0 {
unlock()
return nil
}
if current.TunnelAddr.Compare(details.TunnelAddr.AddrPort) != 0 {
LogDebug.Println("changed udp tunner addr")
oldAddr := current.TunnelAddr
lock.AddrHistory = append(lock.AddrHistory, oldAddr.AddrPort)
}
lock.Udp = &details
}
unlock()
return udp.SendToken(&details)
}
func (udp *UdpTunnel) ResendToken() (bool, error) {
lock, unlock := udp.Details.Read()
defer unlock()
if lock.Udp == nil {
return false, nil
} else if err := udp.SendToken(lock.Udp); err != nil {
return false, err
}
return true, nil
}
func (udp *UdpTunnel) SendToken(details *UdpChannelDetails) error {
if details.TunnelAddr.Addr().Is4() {
udp.Udp4.WriteToUDPAddrPort(details.Token, details.TunnelAddr.AddrPort)
} else {
if udp.Udp6 == nil {
return fmt.Errorf("ipv6 not supported")
}
udp.Udp6.WriteToUDPAddrPort(details.Token, details.TunnelAddr.AddrPort)
}
LogDebug.Printf("send udp session token (len=%d) to %s\n", len(details.Token), details.TunnelAddr.AddrPort.String())
udp.LastSend.Store(now_sec())
return nil
}
func (udp *UdpTunnel) GetSock() (*net.UDPConn, *netip.AddrPort, error) {
lock, unlock := udp.Details.Read()
defer unlock()
if lock.Udp == nil {
LogDebug.Println("udp tunnel not connected")
return nil, nil, fmt.Errorf("udp tunnel not connected")
} else if lock.Udp.TunnelAddr.Addr().Is4() {
return udp.Udp4, &lock.Udp.TunnelAddr.AddrPort, nil
} else if udp.Udp6 == nil {
LogDebug.Println("ipv6 not setup")
return nil, nil, fmt.Errorf("ipv6 not setup")
}
return udp.Udp6, &lock.Udp.TunnelAddr.AddrPort, nil
}
func (Udp *UdpTunnel) Send(data []byte, Flow UdpFlow) (int, error) {
buff := bytes.NewBuffer([]byte{})
if err := Flow.WriteTo(buff); err != nil {
return 0, err
}
socket, addr, err := Udp.GetSock()
if err != nil {
return 0, err
}
return socket.WriteToUDPAddrPort(append(data, buff.Bytes()...), *addr)
}
func (Udp *UdpTunnel) GetToken() ([]byte, error) {
lock, unlock := Udp.Details.Read()
defer unlock()
if lock.Udp == nil {
return nil, fmt.Errorf("udp tunnel not connected")
}
return lock.Udp.Token[:], nil
}
type UdpTunnelRx struct {
ConfirmerdConnection bool
ReceivedPacket *struct {
Bytes uint64
Flow UdpFlow
}
}
func (Udp *UdpTunnel) ReceiveFrom(buff []byte) (*UdpTunnelRx, error) {
udp, tunnelAddr, err := Udp.GetSock()
if err != nil {
return nil, err
}
byteSize, remote, err := udp.ReadFromUDPAddrPort(buff)
if err != nil {
return nil, err
}
if tunnelAddr.Compare(remote) != 0 {
lock, unlock := Udp.Details.Read()
defer unlock()
if !slices.ContainsFunc(lock.AddrHistory, func(a netip.AddrPort) bool {
return a.Compare(remote) == 0
}) {
return nil, fmt.Errorf("got data from other source")
}
}
token, err := Udp.GetToken()
if err != nil {
return nil, err
}
LogDebug.Println("Check token")
LogDebug.Println(buff)
LogDebug.Println(token)
LogDebug.Println("end check token")
if bytes.Equal(buff[:byteSize], token) {
LogDebug.Println("udp session confirmed")
Udp.LastConfirm.Store(now_sec())
return &UdpTunnelRx{ConfirmerdConnection: true}, nil
}
if len(buff) + V6_LEN < byteSize {
return nil, fmt.Errorf("receive buffer too small")
}
footer, footerInt, err := FromTailUdpFlow(buff[byteSize:])
if err != nil {
if footerInt == UDP_CHANNEL_ESTABLISH_ID {
actual := hex.EncodeToString(buff[byteSize:]);
expected := hex.EncodeToString(token);
return nil, fmt.Errorf("unexpected UDP establish packet, actual: %s, expected: %s", actual, expected)
}
return nil, fmt.Errorf("failed to extract udp footer: %s, err: %s", hex.EncodeToString(buff[byteSize:]), err.Error())
}
return &UdpTunnelRx{ReceivedPacket: &struct{Bytes uint64; Flow UdpFlow}{uint64(byteSize) - uint64(footer.Len()), *footer}}, nil
}