platform: Add support for tvOS

This commit is contained in:
世界 2023-07-29 08:37:10 +08:00
parent 1e31d26e03
commit b054441f34
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
9 changed files with 278 additions and 37 deletions

View file

@ -89,8 +89,8 @@ lib:
lib_install: lib_install:
go get -v -d go get -v -d
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230701084532-493ee2e45182 go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230728014906-3de089147f59
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230701084532-493ee2e45182 go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230728014906-3de089147f59
clean: clean:
rm -rf bin dist sing-box rm -rf bin dist sing-box

View file

@ -107,7 +107,7 @@ func buildiOS() {
args := []string{ args := []string{
"bind", "bind",
"-v", "-v",
"-target", "ios,iossimulator,macos", "-target", "ios,iossimulator,tvos,tvossimulator,macos",
"-libname=box", "-libname=box",
} }
if !debugEnabled { if !debugEnabled {

View file

@ -10,7 +10,6 @@ import (
) )
type CommandClient struct { type CommandClient struct {
sharedDirectory string
handler CommandClientHandler handler CommandClientHandler
conn net.Conn conn net.Conn
options CommandClientOptions options CommandClientOptions
@ -29,25 +28,26 @@ type CommandClientHandler interface {
WriteGroups(message OutboundGroupIterator) WriteGroups(message OutboundGroupIterator)
} }
func NewStandaloneCommandClient(sharedDirectory string) *CommandClient { func NewStandaloneCommandClient() *CommandClient {
return &CommandClient{ return new(CommandClient)
sharedDirectory: sharedDirectory,
}
} }
func NewCommandClient(sharedDirectory string, handler CommandClientHandler, options *CommandClientOptions) *CommandClient { func NewCommandClient(sharedDirectory string, handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
return &CommandClient{ return &CommandClient{
sharedDirectory: sharedDirectory,
handler: handler, handler: handler,
options: common.PtrValueOrDefault(options), options: common.PtrValueOrDefault(options),
} }
} }
func (c *CommandClient) directConnect() (net.Conn, error) { func (c *CommandClient) directConnect() (net.Conn, error) {
if !sTVOS {
return net.DialUnix("unix", nil, &net.UnixAddr{ return net.DialUnix("unix", nil, &net.UnixAddr{
Name: filepath.Join(c.sharedDirectory, "command.sock"), Name: filepath.Join(sBasePath, "command.sock"),
Net: "unix", Net: "unix",
}) })
} else {
return net.Dial("tcp", "127.0.0.1:8964")
}
} }
func (c *CommandClient) Connect() error { func (c *CommandClient) Connect() error {

View file

@ -18,7 +18,6 @@ import (
) )
type CommandServer struct { type CommandServer struct {
sockPath string
listener net.Listener listener net.Listener
handler CommandServerHandler handler CommandServerHandler
@ -37,9 +36,8 @@ type CommandServerHandler interface {
ServiceReload() error ServiceReload() error
} }
func NewCommandServer(sharedDirectory string, handler CommandServerHandler, maxLines int32) *CommandServer { func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServer {
server := &CommandServer{ server := &CommandServer{
sockPath: filepath.Join(sharedDirectory, "command.sock"),
handler: handler, handler: handler,
savedLines: new(list.List[string]), savedLines: new(list.List[string]),
maxLines: int(maxLines), maxLines: int(maxLines),
@ -70,20 +68,29 @@ func (s *CommandServer) notifyURLTestUpdate() {
} }
func (s *CommandServer) Start() error { func (s *CommandServer) Start() error {
os.Remove(s.sockPath) if !sTVOS {
return s.listenUNIX()
} else {
return s.listenTCP()
}
}
func (s *CommandServer) listenUNIX() error {
sockPath := filepath.Join(sBasePath, "command.sock")
os.Remove(sockPath)
listener, err := net.ListenUnix("unix", &net.UnixAddr{ listener, err := net.ListenUnix("unix", &net.UnixAddr{
Name: s.sockPath, Name: sockPath,
Net: "unix", Net: "unix",
}) })
if err != nil { if err != nil {
return err return E.Cause(err, "listen")
} }
if sUserID > 0 { if sUserID > 0 {
err = os.Chown(s.sockPath, sUserID, sGroupID) err = os.Chown(sockPath, sUserID, sGroupID)
if err != nil { if err != nil {
listener.Close() listener.Close()
os.Remove(s.sockPath) os.Remove(sockPath)
return err return E.Cause(err, "chown")
} }
} }
s.listener = listener s.listener = listener
@ -91,6 +98,16 @@ func (s *CommandServer) Start() error {
return nil return nil
} }
func (s *CommandServer) listenTCP() error {
listener, err := net.Listen("tcp", "127.0.0.1:8964")
if err != nil {
return E.Cause(err, "listen")
}
s.listener = listener
go s.loopConnection(listener)
return nil
}
func (s *CommandServer) Close() error { func (s *CommandServer) Close() error {
return common.Close( return common.Close(
s.listener, s.listener,

View file

@ -0,0 +1,219 @@
package libbox
import (
"bytes"
"encoding/binary"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw"
)
func EncodeChunkedMessage(data []byte) []byte {
var buffer bytes.Buffer
binary.Write(&buffer, binary.BigEndian, uint16(len(data)))
buffer.Write(data)
return buffer.Bytes()
}
func DecodeLengthChunk(data []byte) int32 {
return int32(binary.BigEndian.Uint16(data))
}
const (
MessageTypeError = iota
MessageTypeProfileList
MessageTypeProfileContentRequest
MessageTypeProfileContent
)
type ErrorMessage struct {
Message string
}
func (e *ErrorMessage) Encode() []byte {
var buffer bytes.Buffer
buffer.WriteByte(MessageTypeError)
rw.WriteVString(&buffer, e.Message)
return buffer.Bytes()
}
func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
reader := bytes.NewReader(data)
messageType, err := rw.ReadByte(reader)
if err != nil {
return nil, err
}
if messageType != MessageTypeError {
return nil, E.New("invalid message")
}
var message ErrorMessage
message.Message, err = rw.ReadVString(reader)
if err != nil {
return nil, err
}
return &message, nil
}
const (
ProfileTypeLocal int32 = iota
ProfileTypeiCloud
ProfileTypeRemote
)
type ProfilePreview struct {
ProfileID int64
Name string
Type int32
}
type ProfilePreviewIterator interface {
Next() *ProfilePreview
HasNext() bool
}
type ProfileEncoder struct {
profiles []ProfilePreview
}
func (e *ProfileEncoder) Append(profile *ProfilePreview) {
e.profiles = append(e.profiles, *profile)
}
func (e *ProfileEncoder) Encode() []byte {
var buffer bytes.Buffer
buffer.WriteByte(MessageTypeProfileList)
binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))
for _, preview := range e.profiles {
binary.Write(&buffer, binary.BigEndian, preview.ProfileID)
rw.WriteVString(&buffer, preview.Name)
binary.Write(&buffer, binary.BigEndian, preview.Type)
}
return buffer.Bytes()
}
type ProfileDecoder struct {
profiles []*ProfilePreview
}
func (d *ProfileDecoder) Decode(data []byte) error {
reader := bytes.NewReader(data)
messageType, err := reader.ReadByte()
if err != nil {
return err
}
if messageType != MessageTypeProfileList {
return E.New("invalid message")
}
var profileCount uint16
err = binary.Read(reader, binary.BigEndian, &profileCount)
if err != nil {
return err
}
for i := 0; i < int(profileCount); i++ {
var profile ProfilePreview
err = binary.Read(reader, binary.BigEndian, &profile.ProfileID)
if err != nil {
return err
}
profile.Name, err = rw.ReadVString(reader)
if err != nil {
return err
}
err = binary.Read(reader, binary.BigEndian, &profile.Type)
if err != nil {
return err
}
d.profiles = append(d.profiles, &profile)
}
return nil
}
func (d *ProfileDecoder) Iterator() ProfilePreviewIterator {
return newIterator(d.profiles)
}
type ProfileContentRequest struct {
ProfileID int64
}
func (r *ProfileContentRequest) Encode() []byte {
var buffer bytes.Buffer
buffer.WriteByte(MessageTypeProfileContentRequest)
binary.Write(&buffer, binary.BigEndian, r.ProfileID)
return buffer.Bytes()
}
func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) {
reader := bytes.NewReader(data)
messageType, err := rw.ReadByte(reader)
if err != nil {
return nil, err
}
if messageType != MessageTypeProfileContentRequest {
return nil, E.New("invalid message")
}
var request ProfileContentRequest
err = binary.Read(reader, binary.BigEndian, &request.ProfileID)
if err != nil {
return nil, err
}
return &request, nil
}
type ProfileContent struct {
Name string
Type int32
Config string
RemotePath string
AutoUpdate bool
LastUpdated int64
}
func (c *ProfileContent) Encode() []byte {
var buffer bytes.Buffer
buffer.WriteByte(MessageTypeProfileContent)
rw.WriteVString(&buffer, c.Name)
binary.Write(&buffer, binary.BigEndian, c.Type)
rw.WriteVString(&buffer, c.Config)
rw.WriteVString(&buffer, c.RemotePath)
binary.Write(&buffer, binary.BigEndian, c.AutoUpdate)
binary.Write(&buffer, binary.BigEndian, c.LastUpdated)
return buffer.Bytes()
}
func DecodeProfileContent(data []byte) (*ProfileContent, error) {
reader := bytes.NewReader(data)
messageType, err := rw.ReadByte(reader)
if err != nil {
return nil, err
}
if messageType != MessageTypeProfileContent {
return nil, E.New("invalid message")
}
var content ProfileContent
content.Name, err = rw.ReadVString(reader)
if err != nil {
return nil, err
}
err = binary.Read(reader, binary.BigEndian, &content.Type)
if err != nil {
return nil, err
}
content.Config, err = rw.ReadVString(reader)
if err != nil {
return nil, err
}
content.RemotePath, err = rw.ReadVString(reader)
if err != nil {
return nil, err
}
err = binary.Read(reader, binary.BigEndian, &content.AutoUpdate)
if err != nil {
return nil, err
}
err = binary.Read(reader, binary.BigEndian, &content.LastUpdated)
if err != nil {
return nil, err
}
return &content, nil
}

View file

@ -33,7 +33,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
return nil, err return nil, err
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
ctx = filemanager.WithDefault(ctx, sBasePath, sTempPath, sUserID, sGroupID) ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage()) ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage())
instance, err := box.New(box.Options{ instance, err := box.New(box.Options{
Context: ctx, Context: ctx,

View file

@ -12,20 +12,25 @@ import (
var ( var (
sBasePath string sBasePath string
sWorkingPath string
sTempPath string sTempPath string
sUserID int sUserID int
sGroupID int sGroupID int
sTVOS bool
) )
func Setup(basePath string, tempPath string) { func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
sBasePath = basePath sBasePath = basePath
sWorkingPath = workingPath
sTempPath = tempPath sTempPath = tempPath
sUserID = os.Getuid() sUserID = os.Getuid()
sGroupID = os.Getgid() sGroupID = os.Getgid()
sTVOS = isTVOS
} }
func SetupWithUsername(basePath string, tempPath string, username string) error { func SetupWithUsername(basePath string, workingPath string, tempPath string, username string) error {
sBasePath = basePath sBasePath = basePath
sWorkingPath = workingPath
sTempPath = tempPath sTempPath = tempPath
sUser, err := user.Lookup(username) sUser, err := user.Lookup(username)
if err != nil { if err != nil {

2
go.mod
View file

@ -21,7 +21,7 @@ require (
github.com/oschwald/maxminddb-golang v1.11.0 github.com/oschwald/maxminddb-golang v1.11.0
github.com/pires/go-proxyproto v0.7.0 github.com/pires/go-proxyproto v0.7.0
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
github.com/sagernet/gomobile v0.0.0-20230701084532-493ee2e45182 github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2
github.com/sagernet/quic-go v0.0.0-20230615020047-10f05c797c02 github.com/sagernet/quic-go v0.0.0-20230615020047-10f05c797c02
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691

4
go.sum
View file

@ -105,8 +105,8 @@ github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I= github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/gomobile v0.0.0-20230701084532-493ee2e45182 h1:sD5g92IO15RAX2DvA4Cq3Uc7tcgqNWVi8K3VTCI6sEo= github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59 h1:vN4divY6LYHcYmiTsCHNPmGZtEsEKJzh81LyvgAQfEQ=
github.com/sagernet/gomobile v0.0.0-20230701084532-493ee2e45182/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU= github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTSWt6hdPrARORfoYvuUczynvRLrueo= github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTSWt6hdPrARORfoYvuUczynvRLrueo=
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA= github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=