tailscale: Add tailssh server

This commit is contained in:
世界
2026-05-28 18:42:58 +08:00
parent 01edf1a64b
commit 9ac1e1b6ca
31 changed files with 2863 additions and 3 deletions
+24
View File
@@ -167,6 +167,30 @@ func (s *platformInterfaceStub) CloseNeighborMonitor(listener adapter.NeighborUp
return nil
}
func (s *platformInterfaceStub) UsePlatformShell() bool {
return false
}
func (s *platformInterfaceStub) CheckPlatformShell() error {
return nil
}
func (s *platformInterfaceStub) OpenShellSession(user *adapter.PlatformUser, command string, env []string, term string, rows int32, cols int32) (adapter.ShellSession, error) {
return nil, os.ErrInvalid
}
func (s *platformInterfaceStub) LookupSFTPServer() (string, error) {
return "", os.ErrInvalid
}
func (s *platformInterfaceStub) ReadSystemSSHHostKey() ([]byte, error) {
return nil, os.ErrInvalid
}
func (s *platformInterfaceStub) LookupUser(username string) (*adapter.PlatformUser, error) {
return nil, os.ErrInvalid
}
func (s *platformInterfaceStub) UsePlatformLocalDNSTransport() bool {
return false
}
@@ -0,0 +1,78 @@
//go:build linux || android || (darwin && !ios)
package libbox
import (
"github.com/sagernet/sing-box/protocol/tailscale/tailssh"
"github.com/sagernet/sing/common"
)
type nativeShellSession struct {
shell *tailssh.Shell
}
func OpenNativeShellSession(
shell, cwd string,
args, environ StringIterator,
term string,
rows, cols, uid, gid int32,
groups Int32Iterator,
) (ShellSession, error) {
sh, err := tailssh.OpenPtyShell(
shell,
iteratorToArray[string](args),
iteratorToArray[string](environ),
cwd,
int(uid), int(gid),
common.Map(iteratorToArray[int32](groups), func(g int32) int { return int(g) }),
uint16(rows), uint16(cols),
)
if err != nil {
return nil, err
}
return &nativeShellSession{shell: sh}, nil
}
func OpenNativePipeSession(
shell, cwd string,
args, environ StringIterator,
uid, gid int32,
groups Int32Iterator,
) (ShellSession, error) {
sh, err := tailssh.OpenSocketpairShell(
shell,
iteratorToArray[string](args),
iteratorToArray[string](environ),
cwd,
int(uid), int(gid),
common.Map(iteratorToArray[int32](groups), func(g int32) int { return int(g) }),
)
if err != nil {
return nil, err
}
return &nativeShellSession{shell: sh}, nil
}
func (s *nativeShellSession) MasterFD() int32 {
return int32(s.shell.MasterFD())
}
func (s *nativeShellSession) Resize(rows, cols int32) error {
return s.shell.Resize(uint16(rows), uint16(cols))
}
func (s *nativeShellSession) Signal(sig int32) error {
return s.shell.Signal(int(sig))
}
func (s *nativeShellSession) WaitExit() (int32, error) {
status, err := s.shell.Wait()
if err != nil {
return 0, err
}
return int32(status), nil
}
func (s *nativeShellSession) Close() error {
return s.shell.Close()
}
@@ -0,0 +1,26 @@
//go:build !linux && !android && (!darwin || ios)
package libbox
import (
E "github.com/sagernet/sing/common/exceptions"
)
func OpenNativeShellSession(
shell, cwd string,
args, environ StringIterator,
term string,
rows, cols, uid, gid int32,
groups Int32Iterator,
) (ShellSession, error) {
return nil, E.New("native shell session not supported on this platform")
}
func OpenNativePipeSession(
shell, cwd string,
args, environ StringIterator,
uid, gid int32,
groups Int32Iterator,
) (ShellSession, error) {
return nil, E.New("native pipe session not supported on this platform")
}
+24
View File
@@ -21,6 +21,30 @@ type PlatformInterface interface {
StartNeighborMonitor(listener NeighborUpdateListener) error
CloseNeighborMonitor(listener NeighborUpdateListener) error
RegisterMyInterface(name string)
UsePlatformShell() bool
CheckPlatformShell() error
OpenShellSession(user *PlatformUser, command string, environ StringIterator, term string, rows int32, cols int32) (ShellSession, error)
LookupUser(username string) (*PlatformUser, error)
LookupSFTPServer() (*StringBox, error)
ReadSystemSSHHostKey() (*StringBox, error)
}
type PlatformUser struct {
Username string
Uid int32
Gid int32
HomeDir string
Shell string
groups []int32
}
func (u *PlatformUser) SetGroups(groups Int32Iterator) {
u.groups = iteratorToArray[int32](groups)
}
func (u *PlatformUser) Groups() Int32Iterator {
return newIterator(u.groups)
}
type NeighborUpdateListener interface {
+57
View File
@@ -247,6 +247,63 @@ func (w *platformInterfaceWrapper) CloseNeighborMonitor(listener adapter.Neighbo
return w.iif.CloseNeighborMonitor(nil)
}
func (w *platformInterfaceWrapper) UsePlatformShell() bool {
return w.iif.UsePlatformShell()
}
func (w *platformInterfaceWrapper) CheckPlatformShell() error {
return w.iif.CheckPlatformShell()
}
func (w *platformInterfaceWrapper) OpenShellSession(user *adapter.PlatformUser, command string, environ []string, term string, rows int32, cols int32) (adapter.ShellSession, error) {
libboxUser := &PlatformUser{
Username: user.Username,
Uid: int32(user.Uid),
Gid: int32(user.Gid),
HomeDir: user.HomeDir,
Shell: user.Shell,
}
if len(user.Groups) > 0 {
libboxUser.SetGroups(newIterator(common.Map(user.Groups, func(g int) int32 { return int32(g) })))
}
session, err := w.iif.OpenShellSession(libboxUser, command, newIterator(environ), term, rows, cols)
if err != nil {
return nil, err
}
return session, nil
}
func (w *platformInterfaceWrapper) LookupSFTPServer() (string, error) {
result, err := w.iif.LookupSFTPServer()
if err != nil {
return "", err
}
return result.Value, nil
}
func (w *platformInterfaceWrapper) ReadSystemSSHHostKey() ([]byte, error) {
result, err := w.iif.ReadSystemSSHHostKey()
if err != nil {
return nil, err
}
return []byte(result.Value), nil
}
func (w *platformInterfaceWrapper) LookupUser(username string) (*adapter.PlatformUser, error) {
platformUser, err := w.iif.LookupUser(username)
if err != nil {
return nil, err
}
return &adapter.PlatformUser{
Username: platformUser.Username,
Uid: int(platformUser.Uid),
Gid: int(platformUser.Gid),
HomeDir: platformUser.HomeDir,
Shell: platformUser.Shell,
Groups: common.Map(iteratorToArray[int32](platformUser.Groups()), func(g int32) int { return int(g) }),
}, nil
}
type neighborUpdateListenerWrapper struct {
listener adapter.NeighborUpdateListener
}
+9
View File
@@ -0,0 +1,9 @@
package libbox
type ShellSession interface {
MasterFD() int32
Resize(rows int32, cols int32) error
Signal(signal int32) error
WaitExit() (int32, error)
Close() error
}