| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 | 
							- package tor
 
- import (
 
- 	"context"
 
- 	"net"
 
- 	"os"
 
- 	"path/filepath"
 
- 	"strings"
 
- 	"github.com/sagernet/sing-box/adapter"
 
- 	"github.com/sagernet/sing-box/adapter/outbound"
 
- 	"github.com/sagernet/sing-box/common/dialer"
 
- 	C "github.com/sagernet/sing-box/constant"
 
- 	"github.com/sagernet/sing-box/log"
 
- 	"github.com/sagernet/sing-box/option"
 
- 	"github.com/sagernet/sing/common"
 
- 	E "github.com/sagernet/sing/common/exceptions"
 
- 	F "github.com/sagernet/sing/common/format"
 
- 	"github.com/sagernet/sing/common/logger"
 
- 	M "github.com/sagernet/sing/common/metadata"
 
- 	N "github.com/sagernet/sing/common/network"
 
- 	"github.com/sagernet/sing/common/rw"
 
- 	"github.com/sagernet/sing/protocol/socks"
 
- 	"github.com/cretz/bine/control"
 
- 	"github.com/cretz/bine/tor"
 
- )
 
- func RegisterOutbound(registry *outbound.Registry) {
 
- 	outbound.Register[option.TorOutboundOptions](registry, C.TypeTor, NewOutbound)
 
- }
 
- type Outbound struct {
 
- 	outbound.Adapter
 
- 	ctx         context.Context
 
- 	logger      logger.ContextLogger
 
- 	proxy       *ProxyListener
 
- 	startConf   *tor.StartConf
 
- 	options     map[string]string
 
- 	events      chan control.Event
 
- 	instance    *tor.Tor
 
- 	socksClient *socks.Client
 
- }
 
- func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TorOutboundOptions) (adapter.Outbound, error) {
 
- 	var startConf tor.StartConf
 
- 	startConf.DataDir = os.ExpandEnv(options.DataDirectory)
 
- 	startConf.TempDataDirBase = os.TempDir()
 
- 	startConf.ExtraArgs = options.ExtraArgs
 
- 	if options.DataDirectory != "" {
 
- 		dataDirAbs, _ := filepath.Abs(startConf.DataDir)
 
- 		if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.IsFile(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
 
- 			options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
 
- 		}
 
- 		if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.IsFile(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
 
- 			options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
 
- 		}
 
- 	}
 
- 	if options.ExecutablePath != "" {
 
- 		startConf.ExePath = options.ExecutablePath
 
- 		startConf.ProcessCreator = nil
 
- 		startConf.UseEmbeddedControlConn = false
 
- 	}
 
- 	if startConf.DataDir != "" {
 
- 		torrcFile := filepath.Join(startConf.DataDir, "torrc")
 
- 		err := rw.MkdirParent(torrcFile)
 
- 		if err != nil {
 
- 			return nil, err
 
- 		}
 
- 		if !rw.IsFile(torrcFile) {
 
- 			err := os.WriteFile(torrcFile, []byte(""), 0o600)
 
- 			if err != nil {
 
- 				return nil, err
 
- 			}
 
- 		}
 
- 		startConf.TorrcFile = torrcFile
 
- 	}
 
- 	outboundDialer, err := dialer.New(ctx, options.DialerOptions)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	return &Outbound{
 
- 		Adapter:   outbound.NewAdapterWithDialerOptions(C.TypeTor, tag, []string{N.NetworkTCP}, options.DialerOptions),
 
- 		ctx:       ctx,
 
- 		logger:    logger,
 
- 		proxy:     NewProxyListener(ctx, logger, outboundDialer),
 
- 		startConf: &startConf,
 
- 		options:   options.Options,
 
- 	}, nil
 
- }
 
- func (t *Outbound) Start() error {
 
- 	err := t.start()
 
- 	if err != nil {
 
- 		t.Close()
 
- 	}
 
- 	return err
 
- }
 
- var torLogEvents = []control.EventCode{
 
- 	control.EventCodeLogDebug,
 
- 	control.EventCodeLogErr,
 
- 	control.EventCodeLogInfo,
 
- 	control.EventCodeLogNotice,
 
- 	control.EventCodeLogWarn,
 
- }
 
- func (t *Outbound) start() error {
 
- 	torInstance, err := tor.Start(t.ctx, t.startConf)
 
- 	if err != nil {
 
- 		return E.New(strings.ToLower(err.Error()))
 
- 	}
 
- 	t.instance = torInstance
 
- 	t.events = make(chan control.Event, 8)
 
- 	err = torInstance.Control.AddEventListener(t.events, torLogEvents...)
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	go t.recvLoop()
 
- 	err = t.proxy.Start()
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	proxyPort := "127.0.0.1:" + F.ToString(t.proxy.Port())
 
- 	proxyUsername := t.proxy.Username()
 
- 	proxyPassword := t.proxy.Password()
 
- 	t.logger.Trace("created upstream proxy at ", proxyPort)
 
- 	t.logger.Trace("upstream proxy username ", proxyUsername)
 
- 	t.logger.Trace("upstream proxy password ", proxyPassword)
 
- 	confOptions := []*control.KeyVal{
 
- 		control.NewKeyVal("Socks5Proxy", proxyPort),
 
- 		control.NewKeyVal("Socks5ProxyUsername", proxyUsername),
 
- 		control.NewKeyVal("Socks5ProxyPassword", proxyPassword),
 
- 	}
 
- 	err = torInstance.Control.ResetConf(confOptions...)
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	if len(t.options) > 0 {
 
- 		for key, value := range t.options {
 
- 			switch key {
 
- 			case "Socks5Proxy",
 
- 				"Socks5ProxyUsername",
 
- 				"Socks5ProxyPassword":
 
- 				continue
 
- 			}
 
- 			err = torInstance.Control.SetConf(control.NewKeyVal(key, value))
 
- 			if err != nil {
 
- 				return E.Cause(err, "set ", key, "=", value)
 
- 			}
 
- 		}
 
- 	}
 
- 	err = torInstance.EnableNetwork(t.ctx, true)
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	info, err := torInstance.Control.GetInfo("net/listeners/socks")
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	if len(info) != 1 || info[0].Key != "net/listeners/socks" {
 
- 		return E.New("get socks proxy address")
 
- 	}
 
- 	t.logger.Trace("obtained tor socks5 address ", info[0].Val)
 
- 	// TODO: set password for tor socks5 server if supported
 
- 	t.socksClient = socks.NewClient(N.SystemDialer, M.ParseSocksaddr(info[0].Val), socks.Version5, "", "")
 
- 	return nil
 
- }
 
- func (t *Outbound) recvLoop() {
 
- 	for rawEvent := range t.events {
 
- 		switch event := rawEvent.(type) {
 
- 		case *control.LogEvent:
 
- 			event.Raw = strings.ToLower(event.Raw)
 
- 			switch event.Severity {
 
- 			case control.EventCodeLogDebug, control.EventCodeLogInfo:
 
- 				t.logger.Trace(event.Raw)
 
- 			case control.EventCodeLogNotice:
 
- 				if strings.Contains(event.Raw, "disablenetwork") || strings.Contains(event.Raw, "socks listener") {
 
- 					t.logger.Trace(event.Raw)
 
- 					continue
 
- 				}
 
- 				t.logger.Info(event.Raw)
 
- 			case control.EventCodeLogWarn:
 
- 				t.logger.Warn(event.Raw)
 
- 			case control.EventCodeLogErr:
 
- 				t.logger.Error(event.Raw)
 
- 			}
 
- 		}
 
- 	}
 
- }
 
- func (t *Outbound) Close() error {
 
- 	err := common.Close(
 
- 		common.PtrOrNil(t.proxy),
 
- 		common.PtrOrNil(t.instance),
 
- 	)
 
- 	if t.events != nil {
 
- 		close(t.events)
 
- 		t.events = nil
 
- 	}
 
- 	return err
 
- }
 
- func (t *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
 
- 	t.logger.InfoContext(ctx, "outbound connection to ", destination)
 
- 	return t.socksClient.DialContext(ctx, network, destination)
 
- }
 
- func (t *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
 
- 	return nil, os.ErrInvalid
 
- }
 
 
  |