mirror of
https://github.com/holdmysocks/ps5-tailscale.git
synced 2026-10-06 14:00:18 +02:00
A payload that runs the Tailscale client (tsnet, userspace networking) on a jailbroken PS5: a C launcher built with ps5-payload-sdk that loads a Go program in-process, an installer that registers it with the console's payload autoloader and adds a home screen icon, and the patch that makes Go 1.27.1 speak the PS5's FreeBSD 9 era syscall interface.
71 lines
2.2 KiB
Go
71 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"net/netip"
|
|
"slices"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// forwardToLocalhost is tsnet's fallback TCP handler: it is asked about every
|
|
// tailnet connection to a port nothing in this process listens on. If a
|
|
// service on the console listens on that port, the connection is accepted and
|
|
// piped to it. That is what makes FTP, the payload loader and the like
|
|
// reachable over the tailnet.
|
|
//
|
|
// The local connection is made before answering, while the tailnet peer is
|
|
// still waiting for its SYN-ACK. If nothing listens on the port the
|
|
// connection is declined, so the peer sees an ordinary "connection refused"
|
|
// rather than a connection that opens and closes.
|
|
func (d *daemon) forwardToLocalhost(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
|
|
port := dst.Port()
|
|
if slices.Contains(d.cfg.BlockedPorts, port) || port == d.proxyPort || d.fwd.listensOnTCP(port) {
|
|
// The outbound proxy and the local forwards are for the console's
|
|
// own apps. Exposing them would let any tailnet device use the
|
|
// console as a relay.
|
|
return nil, false
|
|
}
|
|
local, err := net.DialTimeout("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port))), 2*time.Second)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
// If the tailnet side never completes its handshake the handler is not
|
|
// called; do not keep the local connection open for it forever.
|
|
abandoned := time.AfterFunc(30*time.Second, func() { local.Close() })
|
|
return func(c net.Conn) {
|
|
defer c.Close()
|
|
defer local.Close()
|
|
if !abandoned.Stop() {
|
|
return
|
|
}
|
|
if port != d.webPort {
|
|
// The status page polls every few seconds; logging its own
|
|
// requests would bury everything else.
|
|
d.logf("forward %v -> localhost:%d", src, port)
|
|
}
|
|
pipe(c, local)
|
|
}, true
|
|
}
|
|
|
|
// pipe copies in both directions until both sides are done.
|
|
func pipe(a, b net.Conn) {
|
|
done := make(chan struct{}, 2)
|
|
cp := func(dst, src net.Conn) {
|
|
io.Copy(dst, src)
|
|
// Pass the end of the stream on, so that protocols relying on a
|
|
// half-close (such as sending a payload to the ELF loader) work.
|
|
if cw, ok := dst.(interface{ CloseWrite() error }); ok {
|
|
cw.CloseWrite()
|
|
} else {
|
|
dst.Close()
|
|
}
|
|
done <- struct{}{}
|
|
}
|
|
go cp(a, b)
|
|
go cp(b, a)
|
|
<-done
|
|
<-done
|
|
}
|