playit.gg go implementation #1

Open
Sirherobrine23 wants to merge 6 commits from breaked into main
41 changed files with 2377 additions and 1803 deletions
Showing only changes of commit 37207e9678 - Show all commits

1
.gitignore vendored
View File

@ -22,3 +22,4 @@
go.work go.work
main.go main.go
*.log

View File

@ -1,9 +1,14 @@
package api package api
import ( import (
"log"
"os"
"runtime" "runtime"
) )
var NullFile, _ = os.Open(os.DevNull)
var debug = log.New(NullFile, "api.playit.gg: ", log.Ldate)
const ( const (
GoPlayitVersion string = "0.17.1" GoPlayitVersion string = "0.17.1"
PlayitAPI string = "https://api.playit.gg" // Playit API PlayitAPI string = "https://api.playit.gg" // Playit API
@ -58,11 +63,13 @@ type Api struct {
} }
type PortProto string type PortProto string
func (proto PortProto) IsValid() bool { func (proto PortProto) IsValid() bool {
switch proto { switch proto {
case PortProto(PortTypeBoth): case PortProto(PortTypeBoth):
case PortProto(PortTypeTcp): case PortProto(PortTypeTcp):
case PortProto(PortTypeUdp): return true case PortProto(PortTypeUdp):
return true
} }
return false return false
} }
@ -77,15 +84,23 @@ func (proto PortProto) SetUdp() {
} }
type Platform string type Platform string
func (Platform Platform) Host() { func (Platform Platform) Host() {
switch runtime.GOOS { switch runtime.GOOS {
case "linux": Platform = "linux"; case "linux":
case "freebsd": Platform = "freebsd"; Platform = "linux"
case "windows": Platform = "windows"; case "freebsd":
case "darwin": Platform = "macos"; Platform = "freebsd"
case "android": Platform = "android"; case "windows":
case "ios": Platform = "ios"; Platform = "windows"
default: Platform = "unknown"; case "darwin":
Platform = "macos"
case "android":
Platform = "android"
case "ios":
Platform = "ios"
default:
Platform = "unknown"
} }
} }
func (Platform Platform) Linux() { func (Platform Platform) Linux() {

View File

@ -17,6 +17,18 @@ func recodeJson(from, to any) error {
return nil return nil
} }
func prettyJSON(from string) string {
var data any
if err := json.Unmarshal([]byte(from), &data); err != nil {
return from
}
marData, err := json.MarshalIndent(data, "", " ")
if err != nil {
return from
}
return string(marData)
}
func (w *Api) requestToApi(Path string, Body io.Reader, Response any, Headers map[string]string) (*http.Response, error) { func (w *Api) requestToApi(Path string, Body io.Reader, Response any, Headers map[string]string) (*http.Response, error) {
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", PlayitAPI, Path), Body) req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", PlayitAPI, Path), Body)
if err != nil { if err != nil {
@ -38,16 +50,24 @@ func (w *Api) requestToApi(Path string, Body io.Reader, Response any, Headers ma
return nil, err return nil, err
} }
defer res.Body.Close() defer res.Body.Close()
data, err := io.ReadAll(res.Body)
if err != nil {
return res, err
}
debug.Printf("(%q %d): %s\n", Path, res.StatusCode, prettyJSON(string(data)))
var ResBody struct { var ResBody struct {
Error string `json:"error"`
Status string `json:"status"` Status string `json:"status"`
Data any `json:"data"` Data any `json:"data"`
} }
if err = json.NewDecoder(res.Body).Decode(&ResBody); err != nil { if err = json.Unmarshal(data, &ResBody); err != nil {
return res, err return res, err
} }
if res.StatusCode >= 300 { if res.StatusCode >= 300 {
defer res.Body.Close() if ResBody.Error != "" {
return res, fmt.Errorf("api.playit.gg: %s", ResBody.Error)
}
var errStatus struct { var errStatus struct {
Type string `json:"type"` Type string `json:"type"`
Message string `json:"message"` Message string `json:"message"`

19
logfile/log.go Normal file
View File

@ -0,0 +1,19 @@
package logfile
import (
"encoding/json"
"os"
)
var DebugFile = func() *os.File {
file, err := os.Create("./debug.log")
if err != nil {
panic(err)
}
return file
}()
func JSONString(data any) string {
d, _ := json.Marshal(data)
return string(d)
}

View File

@ -73,5 +73,5 @@ func UdpSocket(SpecialLan bool, Peer, Host netip.AddrPort) (*net.UDPConn, error)
} }
return stream, err return stream, err
} }
return net.DialUDP("udp", nil, net.UDPAddrFromAddrPort(Host)) return net.ListenUDP("udp", nil)
} }

9
network/network.go Normal file
View File

@ -0,0 +1,9 @@
package network
import (
"log"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
)
var debug = log.New(logfile.DebugFile, "network.playit.gg: ", log.Ldate)

View File

@ -89,10 +89,10 @@ func (tcp *TcpClients) Connect(newClient proto.NewClient) (*TcpClient, error) {
return nil, err return nil, err
} }
return &TcpClient{*stream, *droppe}, nil return &TcpClient{stream, droppe}, nil
} }
type TcpClient struct { type TcpClient struct {
Stream net.TCPConn Stream *net.TCPConn
Dropper Dropper Dropper *Dropper
} }

View File

@ -2,11 +2,9 @@ package network
import ( import (
"fmt" "fmt"
"log"
"net" "net"
"net/netip" "net/netip"
"reflect" "reflect"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -18,11 +16,10 @@ type UdpClient struct {
clientKey ClientKey clientKey ClientKey
sendFlow tunnel.UdpFlow sendFlow tunnel.UdpFlow
udpTunnel tunnel.UdpTunnel udpTunnel tunnel.UdpTunnel
localUdp net.UDPConn localUdp *net.UDPConn
localStartAddr netip.AddrPort localStartAddr netip.AddrPort
tunnelFromPort uint16 tunnelFromPort uint16
tunnelToPort uint16 tunnelToPort uint16
udpClientsLock sync.Mutex
udpClients map[ClientKey]UdpClient udpClients map[ClientKey]UdpClient
lastActivity atomic.Uint32 lastActivity atomic.Uint32
} }
@ -47,7 +44,7 @@ func (self *HostToTunnelForwarder) Run() {
self.localUdp.SetReadDeadline(time.Now().Add(time.Second * 30)) self.localUdp.SetReadDeadline(time.Now().Add(time.Second * 30))
size, source, err := self.localUdp.ReadFromUDPAddrPort(buffer) size, source, err := self.localUdp.ReadFromUDPAddrPort(buffer)
if err != nil { if err != nil {
log.Println(err) debug.Println(err)
break break
} else if source.Addr().Compare(self.localStartAddr.Addr()) != 0 { } else if source.Addr().Compare(self.localStartAddr.Addr()) != 0 {
// "dropping packet from different unexpected source" // "dropping packet from different unexpected source"
@ -69,12 +66,10 @@ func (self *HostToTunnelForwarder) Run() {
} }
} }
self.UdpClient.udpClientsLock.Lock()
if _, is := self.UdpClient.udpClients[self.clientKey]; is { if _, is := self.UdpClient.udpClients[self.clientKey]; is {
// if !reflect.DeepEqual(v, self) {} else {} // if !reflect.DeepEqual(v, self) {} else {}
delete(self.UdpClient.udpClients, self.clientKey) delete(self.UdpClient.udpClients, self.clientKey)
} }
self.UdpClient.udpClientsLock.Unlock()
} }
type ClientKey struct { type ClientKey struct {
@ -84,7 +79,6 @@ type ClientKey struct {
type UdpClients struct { type UdpClients struct {
udpTunnel tunnel.UdpTunnel udpTunnel tunnel.UdpTunnel
lookup AddressLookup[netip.AddrPort] lookup AddressLookup[netip.AddrPort]
udpClientsLocker sync.Mutex
udpClients map[ClientKey]UdpClient udpClients map[ClientKey]UdpClient
UseSpecialLan bool UseSpecialLan bool
} }
@ -93,7 +87,6 @@ func NewUdpClients(Tunnel tunnel.UdpTunnel, Lookup AddressLookup[netip.AddrPort]
return UdpClients{ return UdpClients{
udpTunnel: Tunnel, udpTunnel: Tunnel,
lookup: Lookup, lookup: Lookup,
udpClientsLocker: sync.Mutex{},
udpClients: make(map[ClientKey]UdpClient), udpClients: make(map[ClientKey]UdpClient),
UseSpecialLan: true, UseSpecialLan: true,
} }
@ -117,9 +110,6 @@ func (self *UdpClients) ForwardPacket(Flow tunnel.UdpFlow, data []byte) error {
} }
} }
defer self.udpClientsLocker.Unlock()
self.udpClientsLocker.Lock()
client, err := func() (*UdpClient, error) { client, err := func() (*UdpClient, error) {
for kkey, client := range self.udpClients { for kkey, client := range self.udpClients {
if reflect.DeepEqual(kkey, key) { if reflect.DeepEqual(kkey, key) {
@ -151,7 +141,7 @@ func (self *UdpClients) ForwardPacket(Flow tunnel.UdpFlow, data []byte) error {
client := UdpClient{ client := UdpClient{
clientKey: key, clientKey: key,
sendFlow: sendFlow, sendFlow: sendFlow,
localUdp: *usock, localUdp: usock,
udpTunnel: self.udpTunnel, udpTunnel: self.udpTunnel,
localStartAddr: localAddr, localStartAddr: localAddr,
tunnelFromPort: found.FromPort, tunnelFromPort: found.FromPort,

View File

@ -6,6 +6,7 @@ import (
"net/netip" "net/netip"
"sirherobrine23.org/playit-cloud/go-playit/enc" "sirherobrine23.org/playit-cloud/go-playit/enc"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
) )
var ( var (
@ -17,19 +18,24 @@ type ControlFeed struct {
NewClient *NewClient NewClient *NewClient
} }
func (Feed *ControlFeed) ReadFrom(r io.Reader) error { func (Feed *ControlFeed) ReadFrom(r io.Reader) (err error) {
id := enc.ReadU32(r) id := enc.ReadU32(r)
if id == 1 { if id == 1 {
Feed.Response = new(ControlRpcMessage[*ControlResponse]) Feed.Response = new(ControlRpcMessage[*ControlResponse])
Feed.Response.Content = new(ControlResponse) Feed.Response.Content = new(ControlResponse)
return Feed.Response.ReadFrom(r) err = Feed.Response.ReadFrom(r)
debug.Printf("Read Feed (id %d): %s\n", id, logfile.JSONString(Feed))
} else if id == 2 { } else if id == 2 {
Feed.NewClient = &NewClient{} Feed.NewClient = &NewClient{}
return Feed.NewClient.ReadFrom(r) err = Feed.NewClient.ReadFrom(r)
debug.Printf("Read Feed (id %d): %s\n", id, logfile.JSONString(Feed))
} else {
err = ErrFeedRead
} }
return ErrFeedRead return
} }
func (Feed *ControlFeed) WriteTo(w io.Writer) error { func (Feed *ControlFeed) WriteTo(w io.Writer) error {
defer debug.Printf("Write Feed: %s\n", logfile.JSONString(Feed))
if Feed.Response != nil { if Feed.Response != nil {
if err := enc.WriteU32(w, 1); err != nil { if err := enc.WriteU32(w, 1); err != nil {
return err return err

View File

@ -8,6 +8,7 @@ import (
"time" "time"
"sirherobrine23.org/playit-cloud/go-playit/enc" "sirherobrine23.org/playit-cloud/go-playit/enc"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
) )
type ControlRequest struct { type ControlRequest struct {
@ -19,53 +20,65 @@ type ControlRequest struct {
} }
func (Control *ControlRequest) WriteTo(w io.Writer) error { func (Control *ControlRequest) WriteTo(w io.Writer) error {
defer debug.Printf("Write ControlRequest: %s\n", logfile.JSONString(Control))
if Control.Ping != nil { if Control.Ping != nil {
if err := enc.WriteU32(w, 6); err != nil { if err := enc.WriteU32(w, 6); err != nil {
debug.Printf("Write ControlRequest error: %s\n", err.Error())
return err return err
} }
return Control.Ping.WriteTo(w) return Control.Ping.WriteTo(w)
} else if Control.AgentRegister != nil { } else if Control.AgentRegister != nil {
if err := enc.WriteU32(w, 2); err != nil { if err := enc.WriteU32(w, 2); err != nil {
debug.Printf("Write ControlRequest error: %s\n", err.Error())
return err return err
} }
return Control.AgentRegister.WriteTo(w) return Control.AgentRegister.WriteTo(w)
} else if Control.AgentKeepAlive != nil { } else if Control.AgentKeepAlive != nil {
if err := enc.WriteU32(w, 3); err != nil { if err := enc.WriteU32(w, 3); err != nil {
debug.Printf("Write ControlRequest error: %s\n", err.Error())
return err return err
} }
return Control.AgentKeepAlive.WriteTo(w) return Control.AgentKeepAlive.WriteTo(w)
} else if Control.SetupUdpChannel != nil { } else if Control.SetupUdpChannel != nil {
if err := enc.WriteU32(w, 4); err != nil { if err := enc.WriteU32(w, 4); err != nil {
debug.Printf("Write ControlRequest error: %s\n", err.Error())
return err return err
} }
return Control.SetupUdpChannel.WriteTo(w) return Control.SetupUdpChannel.WriteTo(w)
} else if Control.AgentCheckPortMapping != nil { } else if Control.AgentCheckPortMapping != nil {
if err := enc.WriteU32(w, 5); err != nil { if err := enc.WriteU32(w, 5); err != nil {
debug.Printf("Write ControlRequest error: %s\n", err.Error())
return err return err
} }
return Control.AgentCheckPortMapping.WriteTo(w) return Control.AgentCheckPortMapping.WriteTo(w)
} }
return fmt.Errorf("set ControlRequest") return fmt.Errorf("set ControlRequest")
} }
func (Control *ControlRequest) ReadFrom(r io.Reader) error { func (Control *ControlRequest) ReadFrom(r io.Reader) (err error) {
switch enc.ReadU32(r) { switch enc.ReadU32(r) {
case 1: case 1:
Control.Ping = new(Ping) Control.Ping = new(Ping)
return Control.Ping.ReadFrom(r) err = Control.Ping.ReadFrom(r)
case 2: case 2:
Control.AgentRegister = new(AgentRegister) Control.AgentRegister = new(AgentRegister)
return Control.AgentRegister.ReadFrom(r) err = Control.AgentRegister.ReadFrom(r)
case 3: case 3:
Control.AgentKeepAlive = new(AgentSessionId) Control.AgentKeepAlive = new(AgentSessionId)
return Control.AgentKeepAlive.ReadFrom(r) err = Control.AgentKeepAlive.ReadFrom(r)
case 4: case 4:
Control.SetupUdpChannel = new(AgentSessionId) Control.SetupUdpChannel = new(AgentSessionId)
return Control.SetupUdpChannel.ReadFrom(r) err = Control.SetupUdpChannel.ReadFrom(r)
case 5: case 5:
Control.AgentCheckPortMapping = new(AgentCheckPortMapping) Control.AgentCheckPortMapping = new(AgentCheckPortMapping)
return Control.AgentCheckPortMapping.ReadFrom(r) err = Control.AgentCheckPortMapping.ReadFrom(r)
default:
err = fmt.Errorf("invalid ControlRequest id")
} }
return fmt.Errorf("invalid ControlRequest id") debug.Printf("Read ControlRequest: %s\n", logfile.JSONString(Control))
if err != nil {
debug.Printf("Read ControlRequest error: %s\n", err.Error())
}
return
} }
type AgentCheckPortMapping struct { type AgentCheckPortMapping struct {
@ -206,6 +219,7 @@ type ControlResponse struct {
} }
func (Control *ControlResponse) WriteTo(w io.Writer) error { func (Control *ControlResponse) WriteTo(w io.Writer) error {
defer debug.Printf("Write Feed: %s\n", logfile.JSONString(&Control))
if Control.Pong != nil { if Control.Pong != nil {
if err := enc.WriteU32(w, 1); err != nil { if err := enc.WriteU32(w, 1); err != nil {
return err return err
@ -237,34 +251,41 @@ func (Control *ControlResponse) WriteTo(w io.Writer) error {
} }
return fmt.Errorf("insert any options to write") return fmt.Errorf("insert any options to write")
} }
func (Control *ControlResponse) ReadFrom(r io.Reader) error { func (Control *ControlResponse) ReadFrom(r io.Reader) (err error) {
switch enc.ReadU32(r) { code := enc.ReadU32(r)
switch code {
case 1: case 1:
Control.Pong = new(Pong) Control.Pong = new(Pong)
return Control.Pong.ReadFrom(r) err = Control.Pong.ReadFrom(r)
case 2: case 2:
Control.InvalidSignature = true Control.InvalidSignature = true
return nil err = nil
case 3: case 3:
Control.Unauthorized = true Control.Unauthorized = true
return nil err = nil
case 4: case 4:
Control.RequestQueued = true Control.RequestQueued = true
return nil err = nil
case 5: case 5:
Control.TryAgainLater = true Control.TryAgainLater = true
return nil err = nil
case 6: case 6:
Control.AgentRegistered = new(AgentRegistered) Control.AgentRegistered = new(AgentRegistered)
return Control.AgentRegistered.ReadFrom(r) err = Control.AgentRegistered.ReadFrom(r)
case 7: case 7:
Control.AgentPortMapping = new(AgentPortMapping) Control.AgentPortMapping = new(AgentPortMapping)
return Control.AgentPortMapping.ReadFrom(r) err = Control.AgentPortMapping.ReadFrom(r)
case 8: case 8:
Control.UdpChannelDetails = new(UdpChannelDetails) Control.UdpChannelDetails = new(UdpChannelDetails)
return Control.UdpChannelDetails.ReadFrom(r) err = Control.UdpChannelDetails.ReadFrom(r)
default:
err = fmt.Errorf("invalid ControlResponse id")
} }
return fmt.Errorf("invalid ControlResponse id") debug.Printf("Read ControlResponse (Code %d): %s\n", code, logfile.JSONString(Control))
if err != nil {
debug.Printf("Read ControlResponse (Code %d) error: %s\n", code, err.Error())
}
return
} }
type AgentPortMapping struct { type AgentPortMapping struct {

9
proto/proto.go Normal file
View File

@ -0,0 +1,9 @@
package proto
import (
"log"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
)
var debug = log.New(logfile.DebugFile, "proto.playit.gg: ", log.Ldate)

View File

@ -2,8 +2,10 @@ package proto
import ( import (
"io" "io"
"reflect"
"sirherobrine23.org/playit-cloud/go-playit/enc" "sirherobrine23.org/playit-cloud/go-playit/enc"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
) )
type ControlRpcMessage[T MessageEncoding] struct { type ControlRpcMessage[T MessageEncoding] struct {
@ -12,6 +14,8 @@ type ControlRpcMessage[T MessageEncoding] struct {
} }
func (rpc *ControlRpcMessage[T]) WriteTo(w io.Writer) error { func (rpc *ControlRpcMessage[T]) WriteTo(w io.Writer) error {
defer debug.Printf("Write ControlRpcMessage[%s]: %s\n", reflect.TypeOf(rpc.Content).String(), logfile.JSONString(rpc))
if err := enc.WriteU64(w, rpc.RequestID); err != nil { if err := enc.WriteU64(w, rpc.RequestID); err != nil {
return err return err
} else if err = rpc.Content.WriteTo(w); err != nil { } else if err = rpc.Content.WriteTo(w); err != nil {
@ -22,7 +26,9 @@ func (rpc *ControlRpcMessage[T]) WriteTo(w io.Writer) error {
func (rpc *ControlRpcMessage[T]) ReadFrom(r io.Reader) error { func (rpc *ControlRpcMessage[T]) ReadFrom(r io.Reader) error {
rpc.RequestID = enc.ReadU64(r) rpc.RequestID = enc.ReadU64(r)
if err := rpc.Content.ReadFrom(r); err != nil { if err := rpc.Content.ReadFrom(r); err != nil {
debug.Printf("Read ControlRpcMessage[%s] error: %s\n", reflect.TypeOf(rpc.Content).String(), err.Error())
return err return err
} }
debug.Printf("Read ControlRpcMessage[%s]: %s\n", reflect.TypeOf(rpc.Content).String(), logfile.JSONString(rpc))
return nil return nil
} }

View File

@ -1,9 +0,0 @@
package proto
import (
"log"
"os"
)
// Write log and show in terminal to debug
var logDebug *log.Logger = log.New(os.Stderr, "plait.gg", log.Ltime|log.Ldate)

9
runner/runner.go Normal file
View File

@ -0,0 +1,9 @@
package runner
import (
"log"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
)
var debug = log.New(logfile.DebugFile, "runner.playit.gg: ", log.Ldate)

View File

@ -1,7 +1,6 @@
package runner package runner
import ( import (
"fmt"
"io" "io"
"net" "net"
"net/netip" "net/netip"
@ -9,6 +8,7 @@ import (
"time" "time"
"sirherobrine23.org/playit-cloud/go-playit/api" "sirherobrine23.org/playit-cloud/go-playit/api"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
"sirherobrine23.org/playit-cloud/go-playit/network" "sirherobrine23.org/playit-cloud/go-playit/network"
"sirherobrine23.org/playit-cloud/go-playit/tunnel" "sirherobrine23.org/playit-cloud/go-playit/tunnel"
) )
@ -57,11 +57,19 @@ func (self *TunnelRunner) Run() chan error {
continue continue
} }
} }
if new_client := tunnel.Update(); new_client != nil { new_client, err := tunnel.Update()
fmt.Println("New TCP Client") if err != nil {
debug.Printf("Error recived: %s\n", err.Error())
<-time.After(time.Second)
continue
} else if new_client == nil {
<-time.After(time.Second)
continue
}
debug.Println("New TCP Client")
var found *network.AddressValue[netip.AddrPort] var found *network.AddressValue[netip.AddrPort]
if found = self.Lookup.Lookup(new_client.ConnectAddr.Addr(), new_client.ConnectAddr.Port(), api.PortProto("tcp")); found == nil { if found = self.Lookup.Lookup(new_client.ConnectAddr.Addr(), new_client.ConnectAddr.Port(), api.PortProto("tcp")); found == nil {
fmt.Println("could not find local address for connection") debug.Println("could not find local address for connection")
continue continue
} }
go func() { go func() {
@ -74,48 +82,48 @@ func (self *TunnelRunner) Run() chan error {
if tunnel_conn, err = self.TcpClients.Connect(*new_client); err != nil { if tunnel_conn, err = self.TcpClients.Connect(*new_client); err != nil {
return return
} }
if tunnel_conn.Stream != nil {
defer tunnel_conn.Stream.Close() defer tunnel_conn.Stream.Close()
defer tunnel_conn.Dropper.Drop() }
if local_conn, err = network.TcpSocket(self.TcpClients.UseSpecialLAN, new_client.PeerAddr, netip.AddrPortFrom(found.Value.Addr(), (new_client.ConnectAddr.Port()-found.FromPort)+found.Value.Port())); err != nil { if local_conn, err = network.TcpSocket(self.TcpClients.UseSpecialLAN, new_client.PeerAddr, netip.AddrPortFrom(found.Value.Addr(), (new_client.ConnectAddr.Port()-found.FromPort)+found.Value.Port())); err != nil {
fmt.Println(err) debug.Println(err)
return return
} }
defer local_conn.Close() defer local_conn.Close()
done := make(chan struct{}) done := make(chan struct{})
defer close(done) defer close(done)
go func() { go func() {
io.Copy(&tunnel_conn.Stream, local_conn) io.Copy(tunnel_conn.Stream, local_conn)
done <- struct{}{} done <- struct{}{}
}() }()
go func() { go func() {
io.Copy(local_conn, &tunnel_conn.Stream) io.Copy(local_conn, tunnel_conn.Stream)
done <- struct{}{} done <- struct{}{}
}() }()
<-done <-done
<-done <-done
}() }()
} }
}
end <- nil end <- nil
}() }()
go func() { go func() {
udp := tunnel.UdpTunnel() udp := tunnel.UdpTunnel()
for self.KeepRunning.Load() { for self.KeepRunning.Load() {
buffer := make([]byte, 2048) buffer, rx, err := udp.ReceiveFrom()
fmt.Println("udp rec")
rx, err := udp.ReceiveFrom(buffer)
if err != nil { if err != nil {
fmt.Println(err) // if et, is := err.(net.Error); is && !et.Timeout() {
debug.Printf("UdpTunnel Error: %s\n", err.Error())
// }
time.Sleep(time.Second) time.Sleep(time.Second)
continue continue
} }
debug.Printf("UdpTunnel: %s\n", logfile.JSONString(rx))
if rx.ConfirmerdConnection { if rx.ConfirmerdConnection {
continue continue
} } else if err := self.UdpClients.ForwardPacket(rx.ReceivedPacket.Flow, buffer); err != nil {
bytes, flow := rx.ReceivedPacket.Bytes, rx.ReceivedPacket.Flow debug.Println(err)
if err := self.UdpClients.ForwardPacket(flow, buffer[:bytes]); err != nil {
panic(err) panic(err)
} }
} }

View File

@ -20,29 +20,38 @@ type AuthenticatedControl struct {
CurrentPing *uint32 CurrentPing *uint32
} }
func (self *AuthenticatedControl) SendKeepAlive(requestId uint64) error { func (control *AuthenticatedControl) SendKeepAlive(requestID uint64) error {
return self.Send(proto.ControlRpcMessage[*proto.ControlRequest]{ return control.Send(proto.ControlRpcMessage[*proto.ControlRequest]{
requestId, RequestID: requestID,
&proto.ControlRequest{ Content: &proto.ControlRequest{
AgentKeepAlive: &self.Registered.Id, AgentKeepAlive: &control.Registered.Id,
}, },
}) })
} }
func (self *AuthenticatedControl) SendSetupUdpChannel(requestId uint64) error { func (self *AuthenticatedControl) SendSetupUdpChannel(requestId uint64) error {
return self.Send(proto.ControlRpcMessage[*proto.ControlRequest]{ return self.Send(proto.ControlRpcMessage[*proto.ControlRequest]{
requestId, RequestID: requestId,
&proto.ControlRequest{ Content: &proto.ControlRequest{
SetupUdpChannel: &self.Registered.Id, SetupUdpChannel: &self.Registered.Id,
}, },
}) })
} }
func (self *AuthenticatedControl) SendPing(requestId uint64, Now time.Time) error { func (control *AuthenticatedControl) SetupUdpChannel(requestID uint64) error {
return self.Send(proto.ControlRpcMessage[*proto.ControlRequest]{ return control.Send(proto.ControlRpcMessage[*proto.ControlRequest]{
requestId, RequestID: requestID,
&proto.ControlRequest{ Content: &proto.ControlRequest{
Ping: &proto.Ping{Now, self.CurrentPing, &self.Registered.Id}, SetupUdpChannel: &control.Registered.Id,
},
})
}
func (control *AuthenticatedControl) Ping(requestID uint64, Now time.Time) error {
return control.Send(proto.ControlRpcMessage[*proto.ControlRequest]{
RequestID: requestID,
Content: &proto.ControlRequest{
Ping: &proto.Ping{Now: Now, CurrentPing: control.CurrentPing, SessionId: &control.Registered.Id},
}, },
}) })
} }
@ -88,10 +97,13 @@ func (self *AuthenticatedControl) Authenticate() (AuthenticatedControl, error) {
func (self *AuthenticatedControl) RecvFeedMsg() (proto.ControlFeed, error) { func (self *AuthenticatedControl) RecvFeedMsg() (proto.ControlFeed, error) {
buff := make([]byte, 1024) buff := make([]byte, 1024)
// self.Conn.Udp.SetReadDeadline(*new(time.Time)) // self.Conn.Udp.SetReadDeadline(*new(time.Time)) // Remove deadline
self.Conn.Udp.SetReadDeadline(*new(time.Time)) // Remove deadline self.Conn.Udp.SetReadDeadline(time.Now().Add(time.Microsecond * 5))
size, remote, err := self.Conn.Udp.ReadFromUDPAddrPort(buff) size, remote, err := self.Conn.Udp.ReadFromUDPAddrPort(buff)
if err != nil { if err != nil {
if et, is := err.(net.Error); is && !et.Timeout() {
debug.Printf("control reader UDP control: %s", err.Error())
}
return proto.ControlFeed{}, err return proto.ControlFeed{}, err
} else if remote.Compare(self.Conn.ControlAddr) != 0 { } else if remote.Compare(self.Conn.ControlAddr) != 0 {
return proto.ControlFeed{}, fmt.Errorf("invalid remote, expected %q got %q", remote.String(), self.Conn.ControlAddr.String()) return proto.ControlFeed{}, fmt.Errorf("invalid remote, expected %q got %q", remote.String(), self.Conn.ControlAddr.String())
@ -100,6 +112,7 @@ func (self *AuthenticatedControl) RecvFeedMsg() (proto.ControlFeed, error) {
self.buffer.Write(buff[:size]) self.buffer.Write(buff[:size])
feed := proto.ControlFeed{} feed := proto.ControlFeed{}
if err := feed.ReadFrom(self.buffer); err != nil { if err := feed.ReadFrom(self.buffer); err != nil {
debug.Printf("control feed reader: %s", err.Error())
return proto.ControlFeed{}, err return proto.ControlFeed{}, err
} }
if res := feed.Response; res != nil { if res := feed.Response; res != nil {

View File

@ -1,8 +1,7 @@
package tunnel package tunnel
import ( import (
"encoding/json" "net"
"fmt"
"net/netip" "net/netip"
"slices" "slices"
"time" "time"
@ -26,7 +25,7 @@ func getControlAddresses(api api.Api) ([]netip.AddrPort, error) {
type SimpleTunnel struct { type SimpleTunnel struct {
api api.Api api api.Api
controlAddr netip.AddrPort controlAddr netip.AddrPort
controlChannel AuthenticatedControl ControlChannel AuthenticatedControl
udpTunnel *UdpTunnel udpTunnel *UdpTunnel
lastKeepAlive, lastPing, lastPong, lastUdpAuth time.Time lastKeepAlive, lastPing, lastPong, lastUdpAuth time.Time
lastControlTargets []netip.AddrPort lastControlTargets []netip.AddrPort
@ -60,7 +59,7 @@ func (self *SimpleTunnel) Setup() error {
self.lastControlTargets = addresses self.lastControlTargets = addresses
self.controlAddr = setup.ControlAddr self.controlAddr = setup.ControlAddr
self.controlChannel = controlChannel self.ControlChannel = controlChannel
self.udpTunnel = udpTunnel self.udpTunnel = udpTunnel
self.lastKeepAlive = time.UnixMilli(0) self.lastKeepAlive = time.UnixMilli(0)
self.lastPing = time.UnixMilli(0) self.lastPing = time.UnixMilli(0)
@ -97,7 +96,7 @@ func (self *SimpleTunnel) UpdateControlAddr(connected ConnectedControl) (bool, e
if err != nil { if err != nil {
return false, err return false, err
} }
self.controlChannel = controlChannel self.ControlChannel = controlChannel
self.controlAddr = newControlAddr self.controlAddr = newControlAddr
self.lastPing, self.lastKeepAlive, self.lastUdpAuth = time.UnixMilli(0), time.UnixMilli(0), time.UnixMilli(0) self.lastPing, self.lastKeepAlive, self.lastUdpAuth = time.UnixMilli(0), time.UnixMilli(0), time.UnixMilli(0)
self.udpTunnel.InvalidateSession() self.udpTunnel.InvalidateSession()
@ -108,76 +107,85 @@ func (self *SimpleTunnel) UdpTunnel() *UdpTunnel {
return self.udpTunnel return self.udpTunnel
} }
func (self *SimpleTunnel) Update() *proto.NewClient { func (self *SimpleTunnel) Update() (*proto.NewClient, error) {
if self.controlChannel.IsExpired() { if self.ControlChannel.IsExpired() {
auth, err := self.controlChannel.Authenticate() auth, err := self.ControlChannel.Authenticate()
if err != nil { if err != nil {
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
return nil return nil, err
} }
self.controlChannel = auth self.ControlChannel = auth
} }
now := time.Now() now := time.Now()
if now.UnixMilli()-self.lastPing.UnixMilli() > 1_000 { if now.UnixMilli()-self.lastPing.UnixMilli() > 1_000 {
self.lastPing = now self.lastPing = now
if err := self.controlChannel.SendPing(200, now); err != nil { if err := self.ControlChannel.Ping(200, now); err != nil {
debug.Printf("Update: %s\n", err.Error())
return nil, err
} }
} }
if self.udpTunnel.RequiresAuth() { if self.udpTunnel.RequiresAuth() {
if 5_000 < now.UnixMilli()-self.lastUdpAuth.UnixMilli() { if 5_000 < now.UnixMilli()-self.lastUdpAuth.UnixMilli() {
self.lastUdpAuth = now self.lastUdpAuth = now
if err := self.controlChannel.SendSetupUdpChannel(9_000); err != nil { if err := self.ControlChannel.SetupUdpChannel(9_000); err != nil {
debug.Printf("Update: %s\n", err.Error())
return nil, err
} }
} }
} else if self.udpTunnel.RequireResend() { } else if self.udpTunnel.RequireResend() {
if 1_000 < now.UnixMilli()-self.lastUdpAuth.UnixMilli() { if 1_000 < now.UnixMilli()-self.lastUdpAuth.UnixMilli() {
self.lastUdpAuth = now self.lastUdpAuth = now
if _, err := self.udpTunnel.ResendToken(); err != nil { if _, err := self.udpTunnel.ResendToken(); err != nil {
return nil, err
} }
} }
} }
timeTillExpire := max(self.controlChannel.GetExpireAt().UnixMilli(), now.UnixMilli()) - now.UnixMilli() timeTillExpire := max(self.ControlChannel.GetExpireAt().UnixMilli(), now.UnixMilli()) - now.UnixMilli()
if 10_000 < now.UnixMilli()-self.lastKeepAlive.UnixMilli() && timeTillExpire < 30_000 { if 10_000 < now.UnixMilli()-self.lastKeepAlive.UnixMilli() && timeTillExpire < 30_000 {
self.lastKeepAlive = now self.lastKeepAlive = now
if err := self.controlChannel.SendKeepAlive(100); err != nil { if err := self.ControlChannel.SendKeepAlive(100); err != nil {
} return nil, err
if err := self.controlChannel.SendSetupUdpChannel(1); err != nil { } else if err := self.ControlChannel.SendSetupUdpChannel(1); err != nil {
return nil, err
} }
} }
for range 30 { for range 80 {
feed, err := self.controlChannel.RecvFeedMsg() feed, err := self.ControlChannel.RecvFeedMsg()
if err != nil { if err != nil {
fmt.Printf("Update: %s", err.Error()) if es, is := err.(net.Error); is && !es.Timeout() {
debug.Printf("RecvFeedMsg error: %s\n", err.Error())
return nil, err
}
continue continue
} }
d, _ := json.MarshalIndent(feed, "", " ")
fmt.Println(string(d))
if newClient := feed.NewClient; newClient != nil { if newClient := feed.NewClient; newClient != nil {
return newClient return newClient, nil
} else if msg := feed.Response; msg != nil { } else if msg := feed.Response; msg != nil {
if content := msg.Content; content != nil { if content := msg.Content; content != nil {
if details := content.UdpChannelDetails; details != nil { if details := content.UdpChannelDetails; details != nil {
if err := self.udpTunnel.SetUdpTunnel(details); err != nil { if err := self.udpTunnel.SetUdpTunnel(details); err != nil {
panic(err) debug.Printf("Control Recive Message error: %s\n", err.Error())
return nil, err
} }
return self.Update() return self.Update()
} else if content.Unauthorized { } else if content.Unauthorized {
self.controlChannel.SetExpired() self.ControlChannel.SetExpired()
} else if pong := content.Pong; pong != nil { } else if pong := content.Pong; pong != nil {
self.lastPong = time.Now() self.lastPong = time.Now()
// if pong.ClientAddr.Compare(self.controlChannel.Conn.Pong.ClientAddr) != 0 { if pong.ClientAddr.Compare(self.ControlChannel.Conn.Pong.ClientAddr) != 0 {
// fmt.Println("client ip changed", pong.ClientAddr.String(), self.controlChannel.Conn.Pong.ClientAddr.String()) debug.Println("client ip changed", pong.ClientAddr.String(), self.ControlChannel.Conn.Pong.ClientAddr.String())
// } }
} }
} }
} }
} }
if self.lastPong.UnixMilli() != 0 && time.Now().UnixMilli()-self.lastPong.UnixMilli() > 6_000 { if self.lastPong.UnixMilli() != 0 && time.Now().UnixMilli()-self.lastPong.UnixMilli() > 6_000 {
self.lastPong = time.UnixMilli(0) self.lastPong = *new(time.Time)
self.controlChannel.SetExpired() self.ControlChannel.SetExpired()
} }
return nil return nil, nil
} }

View File

@ -12,6 +12,7 @@ type TcpTunnel struct {
} }
func (tcpTunnel *TcpTunnel) Connect() (*net.TCPConn, error) { func (tcpTunnel *TcpTunnel) Connect() (*net.TCPConn, error) {
debug.Printf("Conecting to %q\n", tcpTunnel.ClaimInstruction.Address.String())
conn, err := net.DialTCP("tcp", nil, net.TCPAddrFromAddrPort(tcpTunnel.ClaimInstruction.Address)) conn, err := net.DialTCP("tcp", nil, net.TCPAddrFromAddrPort(tcpTunnel.ClaimInstruction.Address))
if err != nil { if err != nil {
if conn != nil { if conn != nil {

9
tunnel/tunnel.go Normal file
View File

@ -0,0 +1,9 @@
package tunnel
import (
"log"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
)
var debug = log.New(logfile.DebugFile, "tunnel.playit.gg: ", log.Ldate)

View File

@ -73,15 +73,17 @@ func (w *UdpFlow) WriteTo(writer io.Writer) error {
} }
func FromTailUdpFlow(slice []byte) (UdpFlow, uint64, error) { func FromTailUdpFlow(slice []byte) (UdpFlow, uint64, error) {
debug.Printf("FromTailUdpFlow: Avaible bytes: %+v\n", slice)
if len(slice) < 8 { if len(slice) < 8 {
return UdpFlow{}, 0, fmt.Errorf("not space to footer") return UdpFlow{}, 0, fmt.Errorf("not space to footer")
} }
footer := binary.BigEndian.Uint64(slice[(len(slice)-8):]) footer := binary.BigEndian.Uint64(slice[(len(slice)-8):])
debug.Printf("FromTailUdpFlow: Footer %d, bytes: %+v\n", footer, slice[(len(slice)-8):])
if footer == REDIRECT_FLOW_4_FOOTER_ID || footer == REDIRECT_FLOW_4_FOOTER_ID_OLD || footer == (REDIRECT_FLOW_4_FOOTER_ID | REDIRECT_FLOW_4_FOOTER_ID_OLD) { if footer == REDIRECT_FLOW_4_FOOTER_ID || footer == REDIRECT_FLOW_4_FOOTER_ID_OLD || footer == (REDIRECT_FLOW_4_FOOTER_ID | REDIRECT_FLOW_4_FOOTER_ID_OLD) {
if len(slice) < V4_LEN { if len(slice) < V4_LEN {
return UdpFlow{}, 0, fmt.Errorf("v4 not have space") return UdpFlow{}, 0, fmt.Errorf("v4 not have space")
} }
debug.Printf("FromTailUdpFlow: bytes v4: %+v\n", slice[len(slice)-V4_LEN:])
reader := bytes.NewReader(slice[len(slice)-V4_LEN:]) reader := bytes.NewReader(slice[len(slice)-V4_LEN:])
var err error var err error
@ -103,7 +105,8 @@ func FromTailUdpFlow(slice []byte) (UdpFlow, uint64, error) {
if len(slice) < V6_LEN { if len(slice) < V6_LEN {
return UdpFlow{}, footer, fmt.Errorf("v6 not have space") return UdpFlow{}, footer, fmt.Errorf("v6 not have space")
} }
reader := bytes.NewReader(slice[len(slice)-V4_LEN:]) debug.Printf("FromTailUdpFlow: bytes v4: %+v\n", slice[len(slice)-V6_LEN:])
reader := bytes.NewReader(slice[len(slice)-V6_LEN:])
var err error var err error
var src_ip, dst_ip []byte var src_ip, dst_ip []byte
@ -122,5 +125,6 @@ func FromTailUdpFlow(slice []byte) (UdpFlow, uint64, error) {
point.Flow = flow point.Flow = flow
return point, 0, nil return point, 0, nil
} }
debug.Printf("Cannot reader tail udp flow, bytes: %+v\n", slice)
return UdpFlow{}, footer, fmt.Errorf("read fotter") return UdpFlow{}, footer, fmt.Errorf("read fotter")
} }

View File

@ -7,20 +7,18 @@ import (
"net" "net"
"net/netip" "net/netip"
"slices" "slices"
"sync"
"sync/atomic"
"time" "time"
"sirherobrine23.org/playit-cloud/go-playit/logfile"
"sirherobrine23.org/playit-cloud/go-playit/proto" "sirherobrine23.org/playit-cloud/go-playit/proto"
) )
type UdpTunnel struct { type UdpTunnel struct {
Udp4 *net.UDPConn Udp4 *net.UDPConn
Udp6 *net.UDPConn Udp6 *net.UDPConn
locker sync.RWMutex
Details ChannelDetails Details ChannelDetails
LastConfirm atomic.Uint32 LastConfirm uint32
LastSend atomic.Uint32 LastSend uint32
} }
type ChannelDetails struct { type ChannelDetails struct {
@ -48,10 +46,8 @@ func AssignUdpTunnel(tunUdp *UdpTunnel) error {
Udp: nil, Udp: nil,
} }
tunUdp.LastConfirm = atomic.Uint32{} tunUdp.LastConfirm = 0
tunUdp.LastSend = atomic.Uint32{} tunUdp.LastSend = 0
tunUdp.LastConfirm.Store(0)
tunUdp.LastSend.Store(0)
return nil return nil
} }
@ -60,8 +56,8 @@ func (udp *UdpTunnel) IsSetup() bool {
} }
func (udp *UdpTunnel) InvalidateSession() { func (udp *UdpTunnel) InvalidateSession() {
udp.LastConfirm.Store(0) udp.LastConfirm = 0
udp.LastSend.Store(0) udp.LastSend = 0
} }
func now_sec() uint32 { func now_sec() uint32 {
@ -69,13 +65,13 @@ func now_sec() uint32 {
} }
func (udp *UdpTunnel) RequireResend() bool { func (udp *UdpTunnel) RequireResend() bool {
last_confirm := udp.LastConfirm.Load() last_confirm := udp.LastConfirm
/* send token every 10 seconds */ /* send token every 10 seconds */
return 10 < now_sec()-last_confirm return 10 < now_sec()-last_confirm
} }
func (udp *UdpTunnel) RequiresAuth() bool { func (udp *UdpTunnel) RequiresAuth() bool {
lastConf, lastSend := udp.LastConfirm.Load(), udp.LastSend.Load() lastConf, lastSend := udp.LastConfirm, udp.LastSend
if lastSend < lastConf { if lastSend < lastConf {
return false return false
} }
@ -130,7 +126,7 @@ func (udp *UdpTunnel) SendToken(details *proto.UdpChannelDetails) error {
} }
} }
// LogDebug.Printf("send udp session token (len=%d) to %s\n", len(details.Token), details.TunnelAddr.AddrPort.String()) // LogDebug.Printf("send udp session token (len=%d) to %s\n", len(details.Token), details.TunnelAddr.AddrPort.String())
udp.LastSend.Store(now_sec()) udp.LastSend = now_sec()
return nil return nil
} }
@ -184,56 +180,50 @@ type UdpTunnelRx struct {
ReceivedPacket UdpTunnelRxPacket ReceivedPacket UdpTunnelRxPacket
} }
func (Udp *UdpTunnel) ReceiveFrom(buff []byte) (*UdpTunnelRx, error) { func (Udp *UdpTunnel) ReceiveFrom() ([]byte, *UdpTunnelRx, error) {
// Udp.locker.RLock() buff := make([]byte, 2048)
// defer Udp.locker.RUnlock()
udp, tunnelAddr, err := Udp.GetSock() udp, tunnelAddr, err := Udp.GetSock()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// udp.SetReadDeadline(time.Now().Add(time.Second * 2)) udp.SetReadDeadline(time.Now().Add(time.Microsecond * 5))
byteSize, remote, err := udp.ReadFromUDPAddrPort(buff) byteSize, remote, err := udp.ReadFromUDPAddrPort(buff)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} } else if tunnelAddr.Compare(remote) != 0 {
if tunnelAddr.Compare(remote) != 0 { if !slices.ContainsFunc(Udp.Details.AddrHistory, func(a netip.AddrPort) bool {
lock := Udp.Details
if !slices.ContainsFunc(lock.AddrHistory, func(a netip.AddrPort) bool {
return a.Compare(remote) == 0 return a.Compare(remote) == 0
}) { }) {
return nil, fmt.Errorf("got data from other source") return nil, nil, fmt.Errorf("got data from other source")
} }
} }
buff = buff[:byteSize] buff = buff[:byteSize]
token, err := Udp.GetToken() token, err := Udp.GetToken()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
var point UdpTunnelRx point := new(UdpTunnelRx)
if bytes.Equal(buff, token) { if bytes.Equal(buff[:], token) {
// LogDebug.Println("udp session confirmed") debug.Println("udp session confirmed")
Udp.LastConfirm.Store(now_sec()) Udp.LastConfirm = now_sec()
point.ConfirmerdConnection = true point.ConfirmerdConnection = true
return &point, nil return nil, point, nil
} else if len(buff)+V6_LEN < byteSize {
return nil, fmt.Errorf("receive buffer too small")
} }
footer, footerInt, err := FromTailUdpFlow(buff) footer, footerInt, err := FromTailUdpFlow(buff[:])
if err != nil { if err != nil {
debug.Printf("UdpTunnel recive error: %s\n", err.Error())
if footerInt == UDP_CHANNEL_ESTABLISH_ID { if footerInt == UDP_CHANNEL_ESTABLISH_ID {
actual := hex.EncodeToString(buff) actual := hex.EncodeToString(buff)
expected := hex.EncodeToString(token) expected := hex.EncodeToString(token)
return nil, fmt.Errorf("unexpected UDP establish packet, actual: %s, expected: %s", actual, expected) return nil, 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), err.Error()) return nil, nil, fmt.Errorf("failed to extract udp footer: %s, err: %s", hex.EncodeToString(buff), err.Error())
} }
point.ReceivedPacket = UdpTunnelRxPacket{
uint64(byteSize - footer.Len()), point.ReceivedPacket = UdpTunnelRxPacket{uint64(byteSize - footer.Len()), footer}
footer, debug.Printf("UdpTunnel packet: %s\n", logfile.JSONString(point))
} return buff[:point.ReceivedPacket.Bytes], point, nil
return &point, nil
} }