Refactoring-Codes (#5) (#134)

This commit is contained in:
Pk-web6936 authored and GitHub committed 2025-03-28 09:53:10 +08:00
1 parent acb16186b2
commit 6f650d49f3
4 files changed
+127 -154

No files matched your search

+15 -1
View File
@@ -3,7 +3,21 @@ gradle/build.gradle
*.pb.go *.pb.go
binary*.go binary*.go
conf/demo/ conf/demo/
demo/ demo/
assets/ assets/
libv2ray*.[a|j]ar libv2ray*.[a|j]ar
# Ignore binary files
*.exe
*.dll
*.so
*.dylib
# Ignore backup and temporary files
*~
*.swp
*.swo
# Ignore build and log directories
build/
logs/
+44 -54
View File
@@ -35,10 +35,7 @@ const (
xudpBaseKey = "xray.xudp.basekey" xudpBaseKey = "xray.xudp.basekey"
) )
/* // V2RayPoint represents a V2Ray Point Server
V2RayPoint V2Ray Point Server
This is territory of Go, so no getter and setters!
*/
type V2RayPoint struct { type V2RayPoint struct {
SupportSet V2RayVPNServiceSupportsSet SupportSet V2RayVPNServiceSupportsSet
statsManager v2stats.Manager statsManager v2stats.Manager
@@ -55,7 +52,7 @@ type V2RayPoint struct {
AsyncResolve bool AsyncResolve bool
} }
/*V2RayVPNServiceSupportsSet To support Android VPN mode*/ // V2RayVPNServiceSupportsSet is an interface to support Android VPN mode
type V2RayVPNServiceSupportsSet interface { type V2RayVPNServiceSupportsSet interface {
Setup(Conf string) int Setup(Conf string) int
Prepare() int Prepare() int
@@ -64,14 +61,13 @@ type V2RayVPNServiceSupportsSet interface {
OnEmitStatus(int, string) int OnEmitStatus(int, string) int
} }
/*RunLoop Run V2Ray main loop // RunLoop runs the V2Ray main loop
*/
func (v *V2RayPoint) RunLoop(prefIPv6 bool) (err error) { func (v *V2RayPoint) RunLoop(prefIPv6 bool) (err error) {
v.v2rayOP.Lock() v.v2rayOP.Lock()
defer v.v2rayOP.Unlock() defer v.v2rayOP.Unlock()
if v.IsRunning { if v.IsRunning {
return return nil
} }
v.closeChan = make(chan struct{}) v.closeChan = make(chan struct{})
@@ -94,11 +90,12 @@ func (v *V2RayPoint) RunLoop(prefIPv6 bool) (err error) {
return return
} }
// handleResolve handles the resolution process for domains
func (v *V2RayPoint) handleResolve() { func (v *V2RayPoint) handleResolve() {
select { select {
case <-v.dialer.ResolveChan(): case <-v.dialer.ResolveChan():
if !v.dialer.IsVServerReady() { if !v.dialer.IsVServerReady() {
log.Println("vServer cannot resolved, shutdown") log.Println("vServer cannot resolve, shutting down")
v.StopLoop() v.StopLoop()
v.SupportSet.Shutdown() v.SupportSet.Shutdown()
} }
@@ -106,20 +103,20 @@ func (v *V2RayPoint) handleResolve() {
} }
} }
/*StopLoop Stop V2Ray main loop // StopLoop stops the V2Ray main loop
*/ func (v *V2RayPoint) StopLoop() error {
func (v *V2RayPoint) StopLoop() (err error) {
v.v2rayOP.Lock() v.v2rayOP.Lock()
defer v.v2rayOP.Unlock() defer v.v2rayOP.Unlock()
if v.IsRunning { if v.IsRunning {
close(v.closeChan) close(v.closeChan)
v.shutdownInit() v.shutdownInit()
v.SupportSet.OnEmitStatus(0, "Closed") v.SupportSet.OnEmitStatus(0, "Closed")
} }
return return nil
} }
// Delegate Function // QueryStats returns the traffic stats for a given tag and direction
func (v V2RayPoint) QueryStats(tag string, direct string) int64 { func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
if v.statsManager == nil { if v.statsManager == nil {
return 0 return 0
@@ -131,36 +128,36 @@ func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
return counter.Set(0) return counter.Set(0)
} }
// shutdownInit shuts down the V2Ray instance and cleans up resources
func (v *V2RayPoint) shutdownInit() { func (v *V2RayPoint) shutdownInit() {
if v.Vpoint != nil {
v.Vpoint.Close()
v.Vpoint = nil
}
v.IsRunning = false v.IsRunning = false
v.Vpoint.Close()
v.Vpoint = nil
v.statsManager = nil v.statsManager = nil
} }
// pointloop sets up and starts the V2Ray core
func (v *V2RayPoint) pointloop() error { func (v *V2RayPoint) pointloop() error {
log.Println("loading core config") log.Println("Loading core config")
config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent)) config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent))
if err != nil { if err != nil {
log.Println(err) return fmt.Errorf("failed to load core config: %w", err)
return err
} }
log.Println("new core") log.Println("Creating new core instance")
v.Vpoint, err = v2core.New(config) v.Vpoint, err = v2core.New(config)
if err != nil { if err != nil {
v.Vpoint = nil return fmt.Errorf("failed to create core instance: %w", err)
log.Println(err)
return err
} }
v.statsManager = v.Vpoint.GetFeature(v2stats.ManagerType()).(v2stats.Manager) v.statsManager = v.Vpoint.GetFeature(v2stats.ManagerType()).(v2stats.Manager)
log.Println("start core") log.Println("Starting core")
v.IsRunning = true v.IsRunning = true
if err := v.Vpoint.Start(); err != nil { if err := v.Vpoint.Start(); err != nil {
v.IsRunning = false v.IsRunning = false
log.Println(err) return fmt.Errorf("failed to start core: %w", err)
return err
} }
v.SupportSet.Prepare() v.SupportSet.Prepare()
@@ -169,13 +166,14 @@ func (v *V2RayPoint) pointloop() error {
return nil return nil
} }
// MeasureDelay measures the delay to a given URL
func (v *V2RayPoint) MeasureDelay(url string) (int64, error) { func (v *V2RayPoint) MeasureDelay(url string) (int64, error) {
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
defer cancel()
go func() { go func() {
select { select {
case <-v.closeChan: case <-v.closeChan:
// cancel request if close called during measure
cancel() cancel()
case <-ctx.Done(): case <-ctx.Done():
} }
@@ -184,10 +182,8 @@ func (v *V2RayPoint) MeasureDelay(url string) (int64, error) {
return measureInstDelay(ctx, v.Vpoint, url) return measureInstDelay(ctx, v.Vpoint, url)
} }
// InitV2Env set v2 asset path // InitV2Env sets the V2Ray asset path
func InitV2Env(envPath string, key string) { func InitV2Env(envPath string, key string) {
//Initialize asset API, Since Raymond Will not let notify the asset location inside Process,
//We need to set location outside V2Ray
if len(envPath) > 0 { if len(envPath) > 0 {
os.Setenv(v2Asset, envPath) os.Setenv(v2Asset, envPath)
os.Setenv(v2Cert, envPath) os.Setenv(v2Cert, envPath)
@@ -196,7 +192,6 @@ func InitV2Env(envPath string, key string) {
os.Setenv(xudpBaseKey, key) os.Setenv(xudpBaseKey, key)
} }
//Now we handle read, fallback to gomobile asset (apk assets)
v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) { v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) {
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
_, file := filepath.Split(path) _, file := filepath.Split(path)
@@ -206,16 +201,14 @@ func InitV2Env(envPath string, key string) {
} }
} }
// MeasureOutboundDelay measures the outbound delay for a given configuration and URL
func MeasureOutboundDelay(ConfigureFileContent string, url string) (int64, error) { func MeasureOutboundDelay(ConfigureFileContent string, url string) (int64, error) {
config, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent)) config, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent))
if err != nil { if err != nil {
return -1, err return -1, fmt.Errorf("failed to load JSON config: %w", err)
} }
// don't listen to anything for test purpose
config.Inbound = nil config.Inbound = nil
// config.App: (fakedns), log, dispatcher, InboundConfig, OutboundConfig, (stats), router, dns, (policy)
// keep only basic features
var essentialApp []*serial.TypedMessage var essentialApp []*serial.TypedMessage
for _, app := range config.App { for _, app := range config.App {
if app.Type == "xray.app.proxyman.OutboundConfig" || app.Type == "xray.app.dispatcher.Config" || app.Type == "xray.app.log.Config" { if app.Type == "xray.app.proxyman.OutboundConfig" || app.Type == "xray.app.dispatcher.Config" || app.Type == "xray.app.log.Config" {
@@ -226,18 +219,16 @@ func MeasureOutboundDelay(ConfigureFileContent string, url string) (int64, error
inst, err := v2core.New(config) inst, err := v2core.New(config)
if err != nil { if err != nil {
return -1, err return -1, fmt.Errorf("failed to create core instance: %w", err)
} }
inst.Start() inst.Start()
delay, err := measureInstDelay(context.Background(), inst, url) defer inst.Close()
inst.Close() return measureInstDelay(context.Background(), inst, url)
return delay, err
} }
/*NewV2RayPoint new V2RayPoint*/ // NewV2RayPoint creates a new V2RayPoint instance
func NewV2RayPoint(s V2RayVPNServiceSupportsSet, adns bool) *V2RayPoint { func NewV2RayPoint(s V2RayVPNServiceSupportsSet, adns bool) *V2RayPoint {
// inject our own log writer
v2applog.RegisterHandlerCreator(v2applog.LogType_Console, v2applog.RegisterHandlerCreator(v2applog.LogType_Console,
func(lt v2applog.LogType, func(lt v2applog.LogType,
options v2applog.HandlerCreatorOptions) (v2commlog.Handler, error) { options v2applog.HandlerCreatorOptions) (v2commlog.Handler, error) {
@@ -253,18 +244,16 @@ func NewV2RayPoint(s V2RayVPNServiceSupportsSet, adns bool) *V2RayPoint {
} }
} }
/* // CheckVersionX returns the library and V2Ray versions
CheckVersionX string
This func will return libv2ray binding version and V2Ray version used.
*/
func CheckVersionX() string { func CheckVersionX() string {
var version = 30 var version = 30
return fmt.Sprintf("Lib v%d, Xray-core v%s", version, v2core.Version()) return fmt.Sprintf("Lib v%d, Xray-core v%s", version, v2core.Version())
} }
// measureInstDelay measures the delay for an instance to a given URL
func measureInstDelay(ctx context.Context, inst *v2core.Instance, url string) (int64, error) { func measureInstDelay(ctx context.Context, inst *v2core.Instance, url string) (int64, error) {
if inst == nil { if inst == nil {
return -1, errors.New("core instance nil") return -1, errors.New("core instance is nil")
} }
tr := &http.Transport{ tr := &http.Transport{
@@ -279,29 +268,29 @@ func measureInstDelay(ctx context.Context, inst *v2core.Instance, url string) (i
}, },
} }
c := &http.Client{ client := &http.Client{
Transport: tr, Transport: tr,
Timeout: 12 * time.Second, Timeout: 12 * time.Second,
} }
if len(url) <= 0 { if len(url) == 0 {
url = "https://www.google.com/generate_204" url = "https://www.google.com/generate_204"
} }
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
start := time.Now() start := time.Now()
resp, err := c.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return -1, err return -1, err
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
return -1, fmt.Errorf("status != 20x: %s", resp.Status) return -1, fmt.Errorf("unexpected status code: %s", resp.Status)
} }
resp.Body.Close()
return time.Since(start).Milliseconds(), nil return time.Since(start).Milliseconds(), nil
} }
// This struct creates our own log writer without datetime stamp // consoleLogWriter creates our own log writer without datetime stamp
// As Android adds time stamps on each line // As Android adds time stamps on each line
type consoleLogWriter struct { type consoleLogWriter struct {
logger *log.Logger logger *log.Logger
@@ -316,10 +305,11 @@ func (w *consoleLogWriter) Close() error {
return nil return nil
} }
// This logger won't print data/time stamps // createStdoutLogWriter creates a logger that won't print date/time stamps
func createStdoutLogWriter() v2commlog.WriterCreator { func createStdoutLogWriter() v2commlog.WriterCreator {
return func() v2commlog.Writer { return func() v2commlog.Writer {
return &consoleLogWriter{ return &consoleLogWriter{
logger: log.New(os.Stdout, "", 0)} logger: log.New(os.Stdout, "", 0),
}
} }
} }
+37 -62
View File
@@ -17,10 +17,12 @@ import (
v2internet "github.com/xtls/xray-core/transport/internet" v2internet "github.com/xtls/xray-core/transport/internet"
) )
// protectSet defines an interface for protecting sockets
type protectSet interface { type protectSet interface {
Protect(int) bool Protect(int) bool
} }
// resolved holds the resolved IP addresses and associated metadata
type resolved struct { type resolved struct {
domain string domain string
IPs []net.IP IPs []net.IP
@@ -31,27 +33,19 @@ type resolved struct {
lastSwitched time.Time lastSwitched time.Time
} }
// NextIP switch to another resolved result. // NextIP switches to another resolved IP address
// there still be race-condition here if multiple err concurently occured
// may cause idx keep switching,
// but that's an outside error can hardly handled here
func (r *resolved) NextIP() { func (r *resolved) NextIP() {
r.ipLock.Lock() r.ipLock.Lock()
defer r.ipLock.Unlock() defer r.ipLock.Unlock()
if len(r.IPs) > 1 { if len(r.IPs) > 1 {
// throttle, don't switch too quickly
now := time.Now() now := time.Now()
if now.Sub(r.lastSwitched) < time.Second*5 { if now.Sub(r.lastSwitched) < 5*time.Second {
log.Println("switch too quickly") log.Println("switch too quickly")
return return
} }
r.lastSwitched = now r.lastSwitched = now
r.ipIdx++ r.ipIdx++
} else {
return
} }
if r.ipIdx >= uint8(len(r.IPs)) { if r.ipIdx >= uint8(len(r.IPs)) {
@@ -61,27 +55,25 @@ func (r *resolved) NextIP() {
log.Printf("switched to next IP: %v", r.IPs[r.ipIdx]) log.Printf("switched to next IP: %v", r.IPs[r.ipIdx])
} }
// currentIP returns the current IP address
func (r *resolved) currentIP() net.IP { func (r *resolved) currentIP() net.IP {
r.ipLock.Lock() r.ipLock.Lock()
defer r.ipLock.Unlock() defer r.ipLock.Unlock()
if len(r.IPs) > 0 { if len(r.IPs) > 0 {
return r.IPs[r.ipIdx] return r.IPs[r.ipIdx]
} }
return nil return nil
} }
// NewProtectedDialer ... // NewProtectedDialer creates a new ProtectedDialer
func NewProtectedDialer(p protectSet) *ProtectedDialer { func NewProtectedDialer(p protectSet) *ProtectedDialer {
d := &ProtectedDialer{ return &ProtectedDialer{
// prefer native lookup on Android
resolver: &net.Resolver{PreferGo: false}, resolver: &net.Resolver{PreferGo: false},
protectSet: p, protectSet: p,
} }
return d
} }
// ProtectedDialer ... // ProtectedDialer handles protected dialing
type ProtectedDialer struct { type ProtectedDialer struct {
currentServer string currentServer string
resolveChan chan struct{} resolveChan chan struct{}
@@ -93,21 +85,23 @@ type ProtectedDialer struct {
protectSet protectSet
} }
// IsVServerReady checks if the virtual server is ready
func (d *ProtectedDialer) IsVServerReady() bool { func (d *ProtectedDialer) IsVServerReady() bool {
return (d.vServer != nil) return d.vServer != nil
} }
// PrepareResolveChan prepares the resolve channel
func (d *ProtectedDialer) PrepareResolveChan() { func (d *ProtectedDialer) PrepareResolveChan() {
d.resolveChan = make(chan struct{}) d.resolveChan = make(chan struct{})
} }
// ResolveChan returns the resolve channel
func (d *ProtectedDialer) ResolveChan() chan struct{} { func (d *ProtectedDialer) ResolveChan() chan struct{} {
return d.resolveChan return d.resolveChan
} }
// simplicated version of golang: internetAddrList in src/net/ipsock.go // lookupAddr performs DNS resolution for the given address
func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) { func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) {
var ( var (
err error err error
host, port string host, port string
@@ -131,44 +125,41 @@ func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(addrs) == 0 { if len(addrs) == 0 {
return nil, fmt.Errorf("domain %s Failed to resolve", addr) return nil, fmt.Errorf("domain %s Failed to resolve", addr)
} }
IPs := make([]net.IP, 0) IPs := make([]net.IP, 0)
//ipv6 is prefer, append ipv6 then ipv4 if d.preferIPv6 {
//ipv6 is not prefer, append ipv4 then ipv6
if(d.preferIPv6) {
for _, ia := range addrs { for _, ia := range addrs {
if(ia.IP.To4() == nil) { if ia.IP.To4() == nil {
IPs = append(IPs, ia.IP) IPs = append(IPs, ia.IP)
} }
}
}
for _, ia := range addrs {
if(ia.IP.To4() != nil) {
IPs = append(IPs, ia.IP)
} }
} }
if(!d.preferIPv6) { for _, ia := range addrs {
if ia.IP.To4() != nil {
IPs = append(IPs, ia.IP)
}
}
if !d.preferIPv6 {
for _, ia := range addrs { for _, ia := range addrs {
if(ia.IP.To4() == nil) { if ia.IP.To4() == nil {
IPs = append(IPs, ia.IP) IPs = append(IPs, ia.IP)
} }
} }
} }
rs := &resolved{ return &resolved{
domain: host, domain: host,
IPs: IPs, IPs: IPs,
Port: portnum, Port: portnum,
lastResolved: time.Now(), lastResolved: time.Now(),
} }, nil
return rs, nil
} }
// PrepareDomain caches direct v2ray server host // PrepareDomain caches the resolved IP addresses for the given domain
func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct{}, prefIPv6 bool) { func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct{}, prefIPv6 bool) {
log.Printf("Preparing Domain: %s", domainName) log.Printf("Preparing Domain: %s", domainName)
d.currentServer = domainName d.currentServer = domainName
@@ -189,7 +180,7 @@ func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct
case <-closeCh: case <-closeCh:
log.Printf("PrepareDomain exit due to core closed") log.Printf("PrepareDomain exit due to core closed")
return return
case <-time.After(time.Second * 2): case <-time.After(2 * time.Second):
} }
continue continue
} }
@@ -201,6 +192,7 @@ func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct
} }
} }
// getFd returns a file descriptor for the given network
func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) { func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) {
switch network { switch network {
case v2net.Network_TCP: case v2net.Network_TCP:
@@ -208,42 +200,32 @@ func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) {
case v2net.Network_UDP: case v2net.Network_UDP:
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP) fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
default: default:
err = fmt.Errorf("unknow network") err = fmt.Errorf("unknown network")
} }
return return
} }
// Init implement internet.SystemDialer // Init initializes the dialer
func (d *ProtectedDialer) Init(_ dns.Client, _ outbound.Manager) { func (d *ProtectedDialer) Init(_ dns.Client, _ outbound.Manager) {
// do nothing // do nothing
} }
// Dial exported as the protected dial method // Dial performs a protected dial
func (d *ProtectedDialer) Dial(ctx context.Context, func (d *ProtectedDialer) Dial(ctx context.Context,
src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) { src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) {
// network := dest.Network.SystemString()
Address := dest.NetAddr() Address := dest.NetAddr()
// v2ray server address,
// try to connect fixed IP if multiple IP parsed from domain,
// and switch to next IP if error occurred
if Address == d.currentServer { if Address == d.currentServer {
if d.vServer == nil { if d.vServer == nil {
log.Println("Dial pending prepare ...", Address) log.Println("Dial pending prepare ...", Address)
<-d.resolveChan <-d.resolveChan
// user may close connection during PrepareDomain,
// fast return release resources.
if d.vServer == nil { if d.vServer == nil {
return nil, fmt.Errorf("fail to prepare domain %s", d.currentServer) return nil, fmt.Errorf("fail to prepare domain %s", d.currentServer)
} }
} }
// if time.Since(d.vServer.lastResolved) > time.Minute*30 {
// go d.PrepareDomain(Address, nil, d.preferIPv6)
// }
fd, err := d.getFd(dest.Network) fd, err := d.getFd(dest.Network)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -259,8 +241,6 @@ func (d *ProtectedDialer) Dial(ctx context.Context,
return conn, nil return conn, nil
} }
// v2ray connecting to "domestic" servers, no caching results
// log.Printf("Not Using Prepared: %s,%s", network, Address)
resolved, err := d.lookupAddr(Address) resolved, err := d.lookupAddr(Address)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -271,20 +251,18 @@ func (d *ProtectedDialer) Dial(ctx context.Context,
return nil, err return nil, err
} }
// use the first resolved address.
// the result IP may vary, eg: IPv6 addrs comes first if client has ipv6 address
return d.fdConn(ctx, resolved.IPs[0], resolved.Port, dest.Network, fd) return d.fdConn(ctx, resolved.IPs[0], resolved.Port, dest.Network, fd)
} }
// DestIpAddress returns the destination IP address
func (d *ProtectedDialer) DestIpAddress() net.IP { func (d *ProtectedDialer) DestIpAddress() net.IP {
return d.vServer.currentIP() return d.vServer.currentIP()
} }
// fdConn establishes a connection using the given file descriptor
func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, network v2net.Network, fd int) (net.Conn, error) { func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, network v2net.Network, fd int) (net.Conn, error) {
defer unix.Close(fd) defer unix.Close(fd)
// call android VPN service to "protect" the fd connecting straight out
if !d.Protect(fd) { if !d.Protect(fd) {
log.Printf("fdConn fail to protect, Close Fd: %d", fd) log.Printf("fdConn fail to protect, Close Fd: %d", fd)
return nil, errors.New("fail to protect") return nil, errors.New("fail to protect")
@@ -302,19 +280,16 @@ func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, netwo
} }
} else { } else {
if err := unix.Connect(fd, sa); err != nil { if err := unix.Connect(fd, sa); err != nil {
// log.Printf("fdConn unix.Connect err, Close Fd: %d Err: %v", fd, err)
return nil, err return nil, err
} }
} }
file := os.NewFile(uintptr(fd), "Socket") file := os.NewFile(uintptr(fd), "Socket")
if file == nil { if file == nil {
// returned value will be nil if fd is not a valid file descriptor
return nil, errors.New("fdConn fd invalid") return nil, errors.New("fdConn fd invalid")
} }
defer file.Close() defer file.Close()
//Closing conn does not affect file, and closing file does not affect conn.
if network == v2net.Network_UDP { if network == v2net.Network_UDP {
packetConn, err := net.FilePacketConn(file) packetConn, err := net.FilePacketConn(file)
if err != nil { if err != nil {
+31 -37
View File
@@ -12,12 +12,15 @@ import (
v2net "github.com/xtls/xray-core/common/net" v2net "github.com/xtls/xray-core/common/net"
) )
// fakeSupportSet is a mock implementation of the protectSet interface
type fakeSupportSet struct{} type fakeSupportSet struct{}
// Protect is a mock implementation that always returns true
func (f fakeSupportSet) Protect(int) bool { func (f fakeSupportSet) Protect(int) bool {
return true return true
} }
// TestProtectedDialer_PrepareDomain tests the PrepareDomain method of the ProtectedDialer
func TestProtectedDialer_PrepareDomain(t *testing.T) { func TestProtectedDialer_PrepareDomain(t *testing.T) {
type args struct { type args struct {
domainName string domainName string
@@ -26,21 +29,17 @@ func TestProtectedDialer_PrepareDomain(t *testing.T) {
name string name string
args args args args
}{ }{
// TODO: Add test cases. {"Test with baidu.com", args{"baidu.com:80"}},
{"", args{"baidu.com:80"}}, // Add more test cases if needed
// {"", args{"cloudflare.com:443"}},
// {"", args{"apple.com:443"}},
// {"", args{"110.110.110.110:443"}},
// {"", args{"[2002:1234::1]:443"}},
} }
d := NewProtectedDialer(fakeSupportSet{}) d := NewProtectedDialer(fakeSupportSet{})
for _, tt := range tests { for _, tt := range tests {
ch := make(chan struct{})
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ch := make(chan struct{})
go d.PrepareDomain(tt.args.domainName, ch, false) go d.PrepareDomain(tt.args.domainName, ch, false)
time.Sleep(time.Second) time.Sleep(time.Second)
go d.vServer.NextIP() d.vServer.NextIP()
t.Log(d.vServer.currentIP()) t.Log(d.vServer.currentIP())
}) })
} }
@@ -48,19 +47,16 @@ func TestProtectedDialer_PrepareDomain(t *testing.T) {
time.Sleep(time.Second) time.Sleep(time.Second)
} }
// TestProtectedDialer_Dial tests the Dial method of the ProtectedDialer
func TestProtectedDialer_Dial(t *testing.T) { func TestProtectedDialer_Dial(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
wantErr bool wantErr bool
}{ }{
// TODO: Add test cases.
{"baidu.com:80", false}, {"baidu.com:80", false},
{"cloudflare.com:80", false}, {"cloudflare.com:80", false},
{"172.16.192.11:80", true}, {"172.16.192.11:80", true},
// {"172.16.192.10:80", true}, // Add more test cases if needed
// {"[2fff:4322::1]:443", true},
// {"[fc00::1]:443", true},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@@ -84,18 +80,17 @@ func TestProtectedDialer_Dial(t *testing.T) {
t.Log(err) t.Log(err)
return return
} }
_host, _, _ := net.SplitHostPort(tt.name) defer conn.Close()
fmt.Fprintf(conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", _host))
host, _, _ := net.SplitHostPort(tt.name)
fmt.Fprintf(conn, "GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host)
status, err := bufio.NewReader(conn).ReadString('\n') status, err := bufio.NewReader(conn).ReadString('\n')
t.Logf("%#v, %#v\n", status, err) t.Logf("Status: %s, Error: %v", status, err)
conn.Close()
} }
for n := 0; n < 3; n++ { for n := 0; n < 3; n++ {
wg.Add(1) wg.Add(1)
go dial() go dial()
// time.Sleep(time.Millisecond * 10)
// d.pendingMap[tt.name] = make(chan struct{})
} }
wg.Wait() wg.Wait()
@@ -103,6 +98,7 @@ func TestProtectedDialer_Dial(t *testing.T) {
} }
} }
// Test_resolved_NextIP tests the NextIP method of the resolved struct
func Test_resolved_NextIP(t *testing.T) { func Test_resolved_NextIP(t *testing.T) {
type fields struct { type fields struct {
domain string domain string
@@ -113,17 +109,15 @@ func Test_resolved_NextIP(t *testing.T) {
name string name string
fields fields fields fields
}{ }{
// TODO: Add test cases. {"test1", fields{
{"test1", domain: "www.baidu.com",
fields{ IPs: []net.IP{
domain: "www.baidu.com", net.ParseIP("1.2.3.4"),
IPs: []net.IP{ net.ParseIP("4.3.2.1"),
net.ParseIP("1.2.3.4"), net.ParseIP("1234::1"),
net.ParseIP("4.3.2.1"), net.ParseIP("4321::1"),
net.ParseIP("1234::1"), },
net.ParseIP("4321::1"), }},
},
}},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@@ -132,20 +126,20 @@ func Test_resolved_NextIP(t *testing.T) {
IPs: tt.fields.IPs, IPs: tt.fields.IPs,
Port: tt.fields.Port, Port: tt.fields.Port,
} }
t.Logf("%v", r.IPs) t.Logf("Initial IPs: %v", r.IPs)
t.Logf("%v", r.currentIP()) t.Logf("Current IP: %v", r.currentIP())
r.NextIP() r.NextIP()
t.Logf("%v", r.currentIP()) t.Logf("Next IP: %v", r.currentIP())
r.NextIP() r.NextIP()
t.Logf("%v", r.currentIP()) t.Logf("Next IP: %v", r.currentIP())
r.NextIP() r.NextIP()
t.Logf("%v", r.currentIP()) t.Logf("Next IP: %v", r.currentIP())
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
r.NextIP() r.NextIP()
t.Logf("%v", r.currentIP()) t.Logf("Next IP: %v", r.currentIP())
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
r.NextIP() r.NextIP()
t.Logf("%v", r.currentIP()) t.Logf("Next IP: %v", r.currentIP())
}) })
} }
} }