AndroidLibXrayLite/libv2ray_main.go

326 lines
7.4 KiB
Go
Raw Normal View History

2020-11-26 13:35:52 +08:00
package libv2ray
import (
"context"
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"time"
2025-01-07 13:45:25 +08:00
mobasset "golang.org/x/mobile/asset"
2020-11-26 13:35:52 +08:00
2020-12-04 11:10:47 +08:00
v2net "github.com/xtls/xray-core/common/net"
v2filesystem "github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/common/serial"
2024-05-12 11:15:04 +08:00
v2core "github.com/xtls/xray-core/core"
2020-12-04 11:10:47 +08:00
v2stats "github.com/xtls/xray-core/features/stats"
v2serial "github.com/xtls/xray-core/infra/conf/serial"
_ "github.com/xtls/xray-core/main/distro/all"
v2internet "github.com/xtls/xray-core/transport/internet"
2020-11-26 13:35:52 +08:00
2020-12-04 11:10:47 +08:00
v2applog "github.com/xtls/xray-core/app/log"
v2commlog "github.com/xtls/xray-core/common/log"
2020-11-26 13:35:52 +08:00
)
const (
2024-05-12 11:15:04 +08:00
v2Asset = "xray.location.asset"
v2Cert = "xray.location.cert"
2023-12-03 14:28:36 -05:00
xudpBaseKey = "xray.xudp.basekey"
2020-11-26 13:35:52 +08:00
)
2024-05-12 11:15:04 +08:00
/*
V2RayPoint V2Ray Point Server
2020-11-26 13:35:52 +08:00
This is territory of Go, so no getter and setters!
*/
type V2RayPoint struct {
SupportSet V2RayVPNServiceSupportsSet
statsManager v2stats.Manager
2022-06-26 20:18:07 +08:00
dialer *ProtectedDialer
2020-11-26 13:35:52 +08:00
v2rayOP sync.Mutex
closeChan chan struct{}
Vpoint *v2core.Instance
IsRunning bool
DomainName string
ConfigureFileContent string
AsyncResolve bool
}
/*V2RayVPNServiceSupportsSet To support Android VPN mode*/
type V2RayVPNServiceSupportsSet interface {
Setup(Conf string) int
Prepare() int
Shutdown() int
Protect(int) bool
OnEmitStatus(int, string) int
}
/*RunLoop Run V2Ray main loop
*/
func (v *V2RayPoint) RunLoop(prefIPv6 bool) (err error) {
v.v2rayOP.Lock()
defer v.v2rayOP.Unlock()
if v.IsRunning {
return
}
v.closeChan = make(chan struct{})
v.dialer.PrepareResolveChan()
go v.handleResolve()
prepareDomain := func() {
v.dialer.PrepareDomain(v.DomainName, v.closeChan, prefIPv6)
close(v.dialer.ResolveChan())
2020-11-26 13:35:52 +08:00
}
if v.AsyncResolve {
go prepareDomain()
} else {
prepareDomain()
}
err = v.pointloop()
2020-11-26 13:35:52 +08:00
return
}
func (v *V2RayPoint) handleResolve() {
select {
case <-v.dialer.ResolveChan():
if !v.dialer.IsVServerReady() {
log.Println("vServer cannot resolved, shutdown")
v.StopLoop()
v.SupportSet.Shutdown()
}
case <-v.closeChan:
}
}
2020-11-26 13:35:52 +08:00
/*StopLoop Stop V2Ray main loop
*/
func (v *V2RayPoint) StopLoop() (err error) {
v.v2rayOP.Lock()
defer v.v2rayOP.Unlock()
if v.IsRunning {
close(v.closeChan)
v.shutdownInit()
v.SupportSet.OnEmitStatus(0, "Closed")
}
return
}
// Delegate Function
2020-11-26 13:35:52 +08:00
func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
if v.statsManager == nil {
return 0
}
counter := v.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
if counter == nil {
return 0
}
return counter.Set(0)
}
func (v *V2RayPoint) shutdownInit() {
v.IsRunning = false
v.Vpoint.Close()
v.Vpoint = nil
v.statsManager = nil
}
func (v *V2RayPoint) pointloop() error {
2020-11-27 11:26:32 +08:00
log.Println("loading core config")
2020-11-26 13:35:52 +08:00
config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent))
if err != nil {
log.Println(err)
return err
}
2020-11-27 11:26:32 +08:00
log.Println("new core")
2020-11-26 13:35:52 +08:00
v.Vpoint, err = v2core.New(config)
if err != nil {
v.Vpoint = nil
log.Println(err)
return err
}
v.statsManager = v.Vpoint.GetFeature(v2stats.ManagerType()).(v2stats.Manager)
2020-11-27 11:26:32 +08:00
log.Println("start core")
2020-11-26 13:35:52 +08:00
v.IsRunning = true
if err := v.Vpoint.Start(); err != nil {
v.IsRunning = false
log.Println(err)
return err
}
v.SupportSet.Prepare()
v.SupportSet.Setup("")
v.SupportSet.OnEmitStatus(0, "Running")
return nil
}
2024-05-12 11:15:04 +08:00
func (v *V2RayPoint) MeasureDelay(url string) (int64, error) {
2020-11-26 13:35:52 +08:00
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
go func() {
select {
case <-v.closeChan:
// cancel request if close called during measure
2020-11-26 13:35:52 +08:00
cancel()
case <-ctx.Done():
}
}()
2024-05-12 11:15:04 +08:00
return measureInstDelay(ctx, v.Vpoint, url)
2020-11-26 13:35:52 +08:00
}
// InitV2Env set v2 asset path
2023-12-03 14:28:36 -05:00
func InitV2Env(envPath string, key string) {
2020-11-26 13:35:52 +08:00
//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 {
os.Setenv(v2Asset, envPath)
os.Setenv(v2Cert, envPath)
2020-11-26 13:35:52 +08:00
}
2023-12-03 14:28:36 -05:00
if len(key) > 0 {
os.Setenv(xudpBaseKey, key)
}
2020-11-26 13:35:52 +08:00
//Now we handle read, fallback to gomobile asset (apk assets)
v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
_, file := filepath.Split(path)
return mobasset.Open(file)
}
return os.Open(path)
}
}
2024-05-12 11:15:04 +08:00
func MeasureOutboundDelay(ConfigureFileContent string, url string) (int64, error) {
2020-11-26 13:35:52 +08:00
config, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent))
if err != nil {
return -1, err
}
// don't listen to anything for test purpose
2020-11-26 13:35:52 +08:00
config.Inbound = nil
// config.App: (fakedns), log, dispatcher, InboundConfig, OutboundConfig, (stats), router, dns, (policy)
2020-11-26 13:35:52 +08:00
// keep only basic features
var essentialApp []*serial.TypedMessage
for _, app := range config.App {
if app.Type == "xray.app.proxyman.OutboundConfig" || app.Type == "xray.app.dispatcher.Config" || app.Type == "xray.app.log.Config" {
essentialApp = append(essentialApp, app)
}
}
config.App = essentialApp
2020-11-26 13:35:52 +08:00
inst, err := v2core.New(config)
if err != nil {
return -1, err
}
inst.Start()
2024-05-12 11:15:04 +08:00
delay, err := measureInstDelay(context.Background(), inst, url)
2020-11-26 13:35:52 +08:00
inst.Close()
return delay, err
}
/*NewV2RayPoint new V2RayPoint*/
func NewV2RayPoint(s V2RayVPNServiceSupportsSet, adns bool) *V2RayPoint {
// inject our own log writer
v2applog.RegisterHandlerCreator(v2applog.LogType_Console,
func(lt v2applog.LogType,
options v2applog.HandlerCreatorOptions) (v2commlog.Handler, error) {
return v2commlog.NewLogger(createStdoutLogWriter()), nil
})
dialer := NewProtectedDialer(s)
2020-11-26 13:35:52 +08:00
v2internet.UseAlternativeSystemDialer(dialer)
return &V2RayPoint{
SupportSet: s,
dialer: dialer,
AsyncResolve: adns,
}
}
2024-05-12 11:15:04 +08:00
/*
CheckVersionX string
2020-11-26 13:35:52 +08:00
This func will return libv2ray binding version and V2Ray version used.
*/
func CheckVersionX() string {
var version = 30
2022-06-26 20:18:07 +08:00
return fmt.Sprintf("Lib v%d, Xray-core v%s", version, v2core.Version())
2020-11-26 13:35:52 +08:00
}
2024-05-12 11:15:04 +08:00
func measureInstDelay(ctx context.Context, inst *v2core.Instance, url string) (int64, error) {
2020-11-26 13:35:52 +08:00
if inst == nil {
return -1, errors.New("core instance nil")
}
tr := &http.Transport{
TLSHandshakeTimeout: 6 * time.Second,
DisableKeepAlives: true,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dest, err := v2net.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
if err != nil {
return nil, err
}
return v2core.Dial(ctx, inst, dest)
},
}
c := &http.Client{
Transport: tr,
Timeout: 12 * time.Second,
}
2024-05-12 11:15:04 +08:00
if len(url) <= 0 {
url = "https://www.google.com/generate_204"
}
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
2020-11-26 13:35:52 +08:00
start := time.Now()
resp, err := c.Do(req)
if err != nil {
return -1, err
}
2024-05-12 11:15:04 +08:00
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
return -1, fmt.Errorf("status != 20x: %s", resp.Status)
2020-11-26 13:35:52 +08:00
}
resp.Body.Close()
return time.Since(start).Milliseconds(), nil
}
2022-06-26 20:18:07 +08:00
// This struct creates our own log writer without datetime stamp
2022-06-26 20:18:07 +08:00
// As Android adds time stamps on each line
type consoleLogWriter struct {
logger *log.Logger
}
func (w *consoleLogWriter) Write(s string) error {
w.logger.Print(s)
return nil
}
func (w *consoleLogWriter) Close() error {
return nil
}
// This logger won't print data/time stamps
func createStdoutLogWriter() v2commlog.WriterCreator {
return func() v2commlog.Writer {
return &consoleLogWriter{
logger: log.New(os.Stdout, "", 0)}
}
2024-05-12 11:15:04 +08:00
}