mirror of
https://github.com/amnezia-vpn/amnezia-libxray.git
synced 2026-05-17 06:55:44 +03:00
init
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
*.xcframework/
|
||||
*.jar
|
||||
*.aar
|
||||
go.mod
|
||||
go.sum
|
||||
|
||||
main/
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 yiguous
|
||||
Copyright (c) 2023 XTLS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
73
README.md
Normal file
73
README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# libxray
|
||||
|
||||
This is an Xray wrapper focusing on improving the experience of [Xray-core](https://github.com/XTLS/Xray-core) mobile development.
|
||||
|
||||
# Features
|
||||
|
||||
### build.sh
|
||||
|
||||
编译脚本。一键生成 xcframework 和 aar。
|
||||
|
||||
### clash.go
|
||||
|
||||
转换 Clash yaml,Clash.Meta yaml 为 Xray Json。
|
||||
|
||||
### controller.go
|
||||
|
||||
试验性的 Android Protect(fd)。
|
||||
|
||||
### file.go
|
||||
|
||||
文件写入。
|
||||
|
||||
### geo.go
|
||||
|
||||
读取 geosite.data,生成类别名称文件,包含 Attribute。
|
||||
|
||||
读取 geoip.data,生成类别名称文件。
|
||||
|
||||
### memory.go
|
||||
|
||||
强制 GC。
|
||||
|
||||
### ping.go
|
||||
|
||||
测速。
|
||||
|
||||
### port.go
|
||||
|
||||
获取空闲端口。
|
||||
|
||||
### share.go
|
||||
|
||||
转换 v2rayN 订阅为 Xray Json。不支持 VMessQRCode。
|
||||
|
||||
转换 VMessAEAD/VLESS 分享为 Xray Json。
|
||||
|
||||
### uuid.go
|
||||
|
||||
转换自定义文本为 uuid。
|
||||
|
||||
### xray_json.go
|
||||
|
||||
Xray 配置的子集,为出口节点添加了 Name 字段,便于 App 内进行解析。
|
||||
|
||||
### xray.go
|
||||
|
||||
启动和停止 Xray 。
|
||||
|
||||
# Used By
|
||||
|
||||
[FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
||||
|
||||
# Contributing
|
||||
|
||||
[yiguo](https://yiguo.dev) wrote the original source code. Now it belongs to the Xray Community.
|
||||
|
||||
## Credits
|
||||
|
||||
[Project X](https://github.com/xtls/xray-core)
|
||||
|
||||
[VMessPing](https://github.com/v2fly/vmessping)
|
||||
|
||||
[FreePort](https://github.com/phayes/freeport)
|
||||
29
build.sh
Normal file
29
build.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
go mod init libxray
|
||||
go mod tidy
|
||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
gomobile init
|
||||
go get -d golang.org/x/mobile/cmd/gomobile
|
||||
|
||||
build_apple() {
|
||||
rm -fr *.xcframework
|
||||
gomobile bind -target ios
|
||||
mv Libxray.xcframework ios.xcframework
|
||||
gomobile bind -target macos
|
||||
mv Libxray.xcframework macos.xcframework
|
||||
xcodebuild -create-xcframework -framework ios.xcframework/ios-arm64/Libxray.framework -framework ios.xcframework/ios-arm64_x86_64-simulator/Libxray.framework -framework macos.xcframework/macos-arm64_x86_64/Libxray.framework -output Libxray.xcframework
|
||||
}
|
||||
|
||||
build_android() {
|
||||
rm -fr *.jar
|
||||
rm -fr *.aar
|
||||
gomobile bind -target android -androidapi 28
|
||||
}
|
||||
|
||||
echo "will build libxray for $1"
|
||||
if [ "$1" != "apple" ]; then
|
||||
build_android
|
||||
else
|
||||
build_apple
|
||||
fi
|
||||
416
clash.go
Normal file
416
clash.go
Normal file
@@ -0,0 +1,416 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type clashYaml struct {
|
||||
Proxies []clashProxy `yaml:"proxies,omitempty"`
|
||||
}
|
||||
|
||||
type clashProxy struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Type string `yaml:"type,omitempty"`
|
||||
Server string `yaml:"server,omitempty"`
|
||||
Port int `yaml:"port,omitempty"`
|
||||
Uuid string `yaml:"uuid,omitempty"`
|
||||
Cipher string `yaml:"cipher,omitempty"`
|
||||
Username string `yaml:"username,omitempty"`
|
||||
Password string `yaml:"password,omitempty"`
|
||||
|
||||
Tls bool `yaml:"tls,omitempty"`
|
||||
SkipCertVerify bool `yaml:"skip-cert-verify,omitempty"`
|
||||
Servername string `yaml:"servername,omitempty"`
|
||||
Sni string `yaml:"sni,omitempty"`
|
||||
Alpn []string `yaml:"alpn,omitempty"`
|
||||
|
||||
Fingerprint string `yaml:"fingerprint,omitempty"`
|
||||
ClientFingerprint string `yaml:"client-fingerprint,omitempty"`
|
||||
Flow string `yaml:"flow,omitempty"`
|
||||
RealityOpts *clashProxyRealityOpts `yaml:"reality-opts,omitempty"`
|
||||
|
||||
Network string `yaml:"network,omitempty"`
|
||||
Plugin string `yaml:"plugin,omitempty"`
|
||||
PluginOpts *clashProxyPluginOpts `yaml:"plugin-opts,omitempty"`
|
||||
WsOpts *clashProxyWsOpts `yaml:"ws-opts,omitempty"`
|
||||
H2Opts *clashProxyH2Opts `yaml:"h2-opts,omitempty"`
|
||||
GrpcOpts *clashProxyGrpcOpts `yaml:"grpc-opts,omitempty"`
|
||||
}
|
||||
|
||||
type clashProxyPluginOpts struct {
|
||||
Mode string `yaml:"mode,omitempty"`
|
||||
Tls bool `yaml:"tls,omitempty"`
|
||||
Fingerprint string `yaml:"fingerprint,omitempty"`
|
||||
SkipCertVerify bool `yaml:"skip-cert-verify,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Path string `yaml:"path,omitempty"`
|
||||
}
|
||||
|
||||
type clashProxyWsOpts struct {
|
||||
Path string `yaml:"path,omitempty"`
|
||||
Headers *clashProxyWsOptsHeaders `yaml:"headers,omitempty"`
|
||||
MaxEarlyData int `yaml:"max-early-data,omitempty"`
|
||||
EarlyDataHeaderName string `yaml:"early-data-header-name,omitempty"`
|
||||
}
|
||||
|
||||
type clashProxyWsOptsHeaders struct {
|
||||
Host string `yaml:"Host,omitempty"`
|
||||
}
|
||||
|
||||
type clashProxyH2Opts struct {
|
||||
Host []string `yaml:"host,omitempty"`
|
||||
Path string `yaml:"path,omitempty"`
|
||||
}
|
||||
|
||||
type clashProxyGrpcOpts struct {
|
||||
GrpcServiceName string `yaml:"grpc-service-name,omitempty"`
|
||||
}
|
||||
|
||||
type clashProxyRealityOpts struct {
|
||||
PublicKey string `yaml:"public-key,omitempty"`
|
||||
ShortId string `yaml:"short-id,omitempty"`
|
||||
}
|
||||
|
||||
func tryConvertClashYaml(text string) (*xrayJson, error) {
|
||||
clashBytes := []byte(text)
|
||||
clash := clashYaml{}
|
||||
|
||||
err := yaml.Unmarshal(clashBytes, &clash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xray := clash.xrayConfig()
|
||||
return &xray, nil
|
||||
}
|
||||
|
||||
func (clash clashYaml) xrayConfig() xrayJson {
|
||||
var xray xrayJson
|
||||
|
||||
var outbounds []xrayOutbound
|
||||
for _, proxy := range clash.Proxies {
|
||||
if outbound, err := proxy.outbound(); err == nil {
|
||||
outbounds = append(outbounds, *outbound)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
xray.Outbounds = outbounds
|
||||
|
||||
return xray
|
||||
}
|
||||
|
||||
func (proxy clashProxy) outbound() (*xrayOutbound, error) {
|
||||
switch proxy.Type {
|
||||
case "ss":
|
||||
outbound, err := proxy.shadowsocksOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
|
||||
case "vmess":
|
||||
outbound, err := proxy.vmessOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
|
||||
case "vless":
|
||||
outbound, err := proxy.vlessOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
|
||||
case "socks5":
|
||||
outbound, err := proxy.socksOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
case "trojan":
|
||||
outbound, err := proxy.trojanOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupport proxy type: %s", proxy.Type)
|
||||
}
|
||||
|
||||
func (proxy clashProxy) shadowsocksOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "shadowsocks"
|
||||
outbound.Name = proxy.Name
|
||||
|
||||
var server xrayShadowsocksServer
|
||||
server.Address = proxy.Server
|
||||
server.Port = proxy.Port
|
||||
server.Method = proxy.Cipher
|
||||
server.Password = proxy.Password
|
||||
|
||||
var settings xrayShadowsocks
|
||||
settings.Servers = []xrayShadowsocksServer{server}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
if len(proxy.Plugin) != 0 {
|
||||
if proxy.Plugin != "v2ray-plugin" {
|
||||
return nil, fmt.Errorf("unsupport ss plugin: obfs")
|
||||
}
|
||||
if proxy.PluginOpts == nil {
|
||||
return nil, fmt.Errorf("unsupport ss plugin-opts: nil")
|
||||
}
|
||||
if proxy.PluginOpts.Mode != "websocket" {
|
||||
return nil, fmt.Errorf("unsupport ss plugin-opts mode: %s", proxy.PluginOpts.Mode)
|
||||
}
|
||||
var streamSetting xrayStreamSettings
|
||||
streamSetting.Network = "websocket"
|
||||
|
||||
var wsSettings xrayWsSettings
|
||||
if len(proxy.PluginOpts.Path) > 0 {
|
||||
wsSettings.Path = proxy.PluginOpts.Path
|
||||
}
|
||||
if len(proxy.PluginOpts.Host) > 0 {
|
||||
var headers xrayWsSettingsHeaders
|
||||
headers.Host = proxy.PluginOpts.Host
|
||||
wsSettings.Headers = &headers
|
||||
}
|
||||
streamSetting.WsSettings = &wsSettings
|
||||
|
||||
if proxy.PluginOpts.Tls {
|
||||
var tlsSettings xrayTlsSettings
|
||||
tlsSettings.Fingerprint = proxy.PluginOpts.Fingerprint
|
||||
tlsSettings.AllowInsecure = proxy.PluginOpts.SkipCertVerify
|
||||
streamSetting.TlsSettings = &tlsSettings
|
||||
}
|
||||
|
||||
outbound.StreamSettings = &streamSetting
|
||||
}
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy clashProxy) vmessOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "vmess"
|
||||
outbound.Name = proxy.Name
|
||||
|
||||
var user xrayVMessVnextUser
|
||||
user.Id = proxy.Uuid
|
||||
user.Security = proxy.Cipher
|
||||
|
||||
var vnext xrayVMessVnext
|
||||
vnext.Address = proxy.Server
|
||||
vnext.Port = proxy.Port
|
||||
vnext.Users = []xrayVMessVnextUser{user}
|
||||
|
||||
var settings xrayVMess
|
||||
settings.Vnext = []xrayVMessVnext{vnext}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
streamSettings, err := proxy.streamSettings(outbound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.StreamSettings = streamSettings
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy clashProxy) vlessOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "vless"
|
||||
outbound.Name = proxy.Name
|
||||
|
||||
var user xrayVLESSVnextUser
|
||||
user.Id = proxy.Uuid
|
||||
user.Flow = proxy.Flow
|
||||
|
||||
var vnext xrayVLESSVnext
|
||||
vnext.Address = proxy.Server
|
||||
vnext.Port = proxy.Port
|
||||
vnext.Users = []xrayVLESSVnextUser{user}
|
||||
|
||||
var settings xrayVLESS
|
||||
settings.Vnext = []xrayVLESSVnext{vnext}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
streamSettings, err := proxy.streamSettings(outbound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.StreamSettings = streamSettings
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy clashProxy) socksOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "socks"
|
||||
outbound.Name = proxy.Name
|
||||
|
||||
var user xraySocksServerUser
|
||||
user.User = proxy.Username
|
||||
user.Pass = proxy.Password
|
||||
|
||||
var server xraySocksServer
|
||||
server.Address = proxy.Server
|
||||
server.Port = proxy.Port
|
||||
server.Users = []xraySocksServerUser{user}
|
||||
|
||||
var settings xraySocks
|
||||
settings.Servers = []xraySocksServer{server}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
streamSettings, err := proxy.streamSettings(outbound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.StreamSettings = streamSettings
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy clashProxy) trojanOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "trojan"
|
||||
outbound.Name = proxy.Name
|
||||
|
||||
var server xrayTrojanServer
|
||||
server.Address = proxy.Server
|
||||
server.Port = proxy.Port
|
||||
server.Password = proxy.Password
|
||||
|
||||
var settings xrayTrojan
|
||||
settings.Servers = []xrayTrojanServer{server}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
streamSettings, err := proxy.streamSettings(outbound)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.StreamSettings = streamSettings
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy clashProxy) streamSettings(outbound xrayOutbound) (*xrayStreamSettings, error) {
|
||||
var streamSettings xrayStreamSettings
|
||||
if len(proxy.Network) == 0 {
|
||||
streamSettings.Network = "tcp"
|
||||
} else {
|
||||
streamSettings.Network = proxy.Network
|
||||
}
|
||||
|
||||
switch streamSettings.Network {
|
||||
case "ws":
|
||||
if proxy.WsOpts != nil {
|
||||
var wsSettings xrayWsSettings
|
||||
if proxy.WsOpts.Headers != nil {
|
||||
var headers xrayWsSettingsHeaders
|
||||
headers.Host = proxy.WsOpts.Headers.Host
|
||||
wsSettings.Headers = &headers
|
||||
}
|
||||
|
||||
wsSettings.Path = proxy.WsOpts.Path
|
||||
|
||||
if proxy.WsOpts.MaxEarlyData > 0 {
|
||||
return nil, fmt.Errorf("unsupport ws-opts max-early-data: %d", proxy.WsOpts.MaxEarlyData)
|
||||
}
|
||||
streamSettings.WsSettings = &wsSettings
|
||||
}
|
||||
case "h2":
|
||||
if proxy.H2Opts != nil {
|
||||
var httpSettings xrayHttpSettings
|
||||
httpSettings.Host = proxy.H2Opts.Host
|
||||
httpSettings.Path = proxy.H2Opts.Path
|
||||
|
||||
streamSettings.HttpSettings = &httpSettings
|
||||
}
|
||||
case "grpc":
|
||||
if proxy.GrpcOpts != nil {
|
||||
var grpcSettings xrayGrpcSettings
|
||||
grpcSettings.ServiceName = proxy.GrpcOpts.GrpcServiceName
|
||||
|
||||
streamSettings.GrpcSettings = &grpcSettings
|
||||
}
|
||||
}
|
||||
proxy.parseSecurity(&streamSettings, outbound)
|
||||
return &streamSettings, nil
|
||||
}
|
||||
|
||||
func (proxy clashProxy) parseSecurity(streamSettings *xrayStreamSettings, outbound xrayOutbound) {
|
||||
var tlsSettings xrayTlsSettings
|
||||
var realitySettings xrayRealitySettings
|
||||
|
||||
if proxy.Tls {
|
||||
streamSettings.Security = "tls"
|
||||
if proxy.SkipCertVerify {
|
||||
tlsSettings.AllowInsecure = true
|
||||
}
|
||||
}
|
||||
if proxy.RealityOpts != nil {
|
||||
streamSettings.Security = "reality"
|
||||
realitySettings.PublicKey = proxy.RealityOpts.PublicKey
|
||||
realitySettings.ShortId = proxy.RealityOpts.ShortId
|
||||
}
|
||||
if len(proxy.Servername) > 0 {
|
||||
tlsSettings.ServerName = proxy.Servername
|
||||
realitySettings.ServerName = proxy.Servername
|
||||
}
|
||||
if len(proxy.Sni) > 0 {
|
||||
tlsSettings.ServerName = proxy.Sni
|
||||
realitySettings.ServerName = proxy.Sni
|
||||
}
|
||||
if len(proxy.Alpn) > 0 {
|
||||
tlsSettings.Alpn = proxy.Alpn
|
||||
}
|
||||
if len(proxy.Fingerprint) > 0 {
|
||||
tlsSettings.Fingerprint = proxy.Fingerprint
|
||||
realitySettings.Fingerprint = proxy.Fingerprint
|
||||
}
|
||||
if len(proxy.ClientFingerprint) > 0 {
|
||||
tlsSettings.Fingerprint = proxy.ClientFingerprint
|
||||
realitySettings.Fingerprint = proxy.ClientFingerprint
|
||||
}
|
||||
|
||||
if outbound.Protocol == "trojan" && len(streamSettings.Security) == 0 {
|
||||
streamSettings.Security = "tls"
|
||||
}
|
||||
|
||||
switch streamSettings.Security {
|
||||
case "tls":
|
||||
streamSettings.TlsSettings = &tlsSettings
|
||||
case "reality":
|
||||
streamSettings.RealitySettings = &realitySettings
|
||||
}
|
||||
}
|
||||
16
controller.go
Normal file
16
controller.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
xinternet "github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
type DialerController interface {
|
||||
FdCallback(int) bool
|
||||
}
|
||||
|
||||
func RegisterDialerController(controller DialerController) {
|
||||
xinternet.RegisterDialerController(func(network, address string, fd uintptr) error {
|
||||
controller.FdCallback(int(fd))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
33
file.go
Normal file
33
file.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func writeBytes(bytes []byte, path string) error {
|
||||
fi, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
_, err = fi.Write(bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeText(text string, path string) error {
|
||||
fi, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0664)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
_, err = fi.WriteString(text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
95
geo.go
Normal file
95
geo.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func LoadGeoData(dir string) string {
|
||||
if err := loadGeoSite(dir); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if err := loadGeoIP(dir); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
ts := time.Now().Unix()
|
||||
tsText := strconv.FormatInt(ts, 10)
|
||||
tsPath := path.Join(dir, "timestamp.txt")
|
||||
if err := writeText(tsText, tsPath); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func loadGeoSite(dir string) error {
|
||||
datPath := path.Join(dir, "geosite.dat")
|
||||
txtPath := path.Join(dir, "geosite.txt")
|
||||
geositeBytes, err := filesystem.ReadFile(datPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var geositeList router.GeoSiteList
|
||||
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var countries []string
|
||||
for _, site := range geositeList.Entry {
|
||||
countries = append(countries, site.CountryCode)
|
||||
for _, domain := range site.Domain {
|
||||
for _, attribute := range domain.Attribute {
|
||||
attr := fmt.Sprintf("%s@%s", site.CountryCode, attribute.Key)
|
||||
if !containsString(countries, attr) {
|
||||
countries = append(countries, attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text := strings.Join(countries, "\n")
|
||||
if err := writeText(text, txtPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadGeoIP(dir string) error {
|
||||
datPath := path.Join(dir, "geoip.dat")
|
||||
txtPath := path.Join(dir, "geoip.txt")
|
||||
geoipBytes, err := filesystem.ReadFile(datPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var geoipList router.GeoIPList
|
||||
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var countries []string
|
||||
for _, geoip := range geoipList.Entry {
|
||||
countries = append(countries, geoip.CountryCode)
|
||||
}
|
||||
|
||||
text := strings.Join(countries, "\n")
|
||||
if err := writeText(text, txtPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func containsString(slice []string, element string) bool {
|
||||
for _, e := range slice {
|
||||
if e == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
39
memory.go
Normal file
39
memory.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
)
|
||||
|
||||
// will be removed when 1.9.0 released
|
||||
|
||||
func forceFree(interval time.Duration) {
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(interval)
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func readForceFreeInterval() int {
|
||||
const key = "XRAY_MEMORY_FORCEFREE"
|
||||
const defaultValue = 0
|
||||
interval := platform.EnvFlag{
|
||||
Name: key,
|
||||
AltName: platform.NormalizeEnvName(key),
|
||||
}.GetValueAsInt(defaultValue)
|
||||
return interval
|
||||
}
|
||||
|
||||
func initForceFree(maxMemory int64) {
|
||||
debug.SetGCPercent(10)
|
||||
debug.SetMemoryLimit(maxMemory)
|
||||
interval := readForceFreeInterval()
|
||||
if interval > 0 {
|
||||
duration := time.Duration(interval) * time.Second
|
||||
forceFree(duration)
|
||||
}
|
||||
}
|
||||
93
ping.go
Normal file
93
ping.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
xnet "github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/core"
|
||||
)
|
||||
|
||||
const (
|
||||
pingDelayTimeout int64 = 11000
|
||||
pingDelayError int64 = 10000
|
||||
)
|
||||
|
||||
func Ping(datDir string, config string, timeout int, url string, times int) string {
|
||||
initEnv(datDir)
|
||||
server, err := startXray(config)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%d:%s", pingDelayError, err)
|
||||
}
|
||||
|
||||
if err := server.Start(); err != nil {
|
||||
return fmt.Sprintf("%d:%s", pingDelayError, err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
return measureDelay(server, timeout, url, times)
|
||||
}
|
||||
|
||||
func measureDelay(inst *core.Instance, timeout int, url string, times int) string {
|
||||
httpTimeout := time.Second * time.Duration(timeout)
|
||||
c, err := coreHTTPClient(inst, httpTimeout)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%d:%s", pingDelayError, err)
|
||||
}
|
||||
delaySum := int64(0)
|
||||
count := int64(0)
|
||||
isValid := false
|
||||
lastErr := ""
|
||||
for i := 0; i < times; i++ {
|
||||
delay, err := coreHTTPRequest(c, url)
|
||||
if delay != pingDelayTimeout {
|
||||
delaySum += delay
|
||||
count += 1
|
||||
isValid = true
|
||||
} else {
|
||||
lastErr = err.Error()
|
||||
}
|
||||
}
|
||||
if !isValid {
|
||||
return fmt.Sprintf("%d:%s", pingDelayTimeout, lastErr)
|
||||
}
|
||||
return fmt.Sprintf("%d:%s", delaySum/count, lastErr)
|
||||
}
|
||||
|
||||
func coreHTTPClient(inst *core.Instance, timeout time.Duration) (*http.Client, error) {
|
||||
if inst == nil {
|
||||
return nil, errors.New("core instance nil")
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := xnet.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return core.Dial(ctx, inst, dest)
|
||||
},
|
||||
}
|
||||
|
||||
c := &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func coreHTTPRequest(c *http.Client, url string) (int64, error) {
|
||||
start := time.Now()
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
_, err := c.Do(req)
|
||||
if err != nil {
|
||||
return pingDelayTimeout, err
|
||||
}
|
||||
return time.Since(start).Milliseconds(), nil
|
||||
}
|
||||
27
port.go
Normal file
27
port.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// https://github.com/phayes/freeport/blob/master/freeport.go
|
||||
// GetFreePort asks the kernel for free open ports that are ready to use.
|
||||
func GetFreePorts(count int) string {
|
||||
var ports []int
|
||||
for i := 0; i < count; i++ {
|
||||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer l.Close()
|
||||
ports = append(ports, l.Addr().(*net.TCPAddr).Port)
|
||||
}
|
||||
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(ports)), ":"), "[]")
|
||||
}
|
||||
487
share.go
Normal file
487
share.go
Normal file
@@ -0,0 +1,487 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
)
|
||||
|
||||
func ParseShareText(textPath string, xrayPath string) string {
|
||||
textBytes, err := filesystem.ReadFile(textPath)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
text := string(textBytes)
|
||||
text = strings.TrimSpace(text)
|
||||
|
||||
if strings.HasPrefix(text, "{") {
|
||||
if err = filesystem.CopyFile(xrayPath, textPath); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
text = checkWindowsReturn(text)
|
||||
if strings.HasPrefix(text, "vless://") || strings.HasPrefix(text, "vmess://") || strings.HasPrefix(text, "socks://") || strings.HasPrefix(text, "ss://") || strings.HasPrefix(text, "trojan://") {
|
||||
xray, err := parsePlainShareText(text)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
err = writeXrayJson(xray, xrayPath)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
} else {
|
||||
xray, err := tryParse(text)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
err = writeXrayJson(xray, xrayPath)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func checkWindowsReturn(text string) string {
|
||||
if strings.Contains(text, "\r\n") {
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func parsePlainShareText(text string) (*xrayJson, error) {
|
||||
proxies := strings.Split(text, "\n")
|
||||
|
||||
var xray xrayJson
|
||||
var outbounds []xrayOutbound
|
||||
for _, proxy := range proxies {
|
||||
link, err := url.Parse(proxy)
|
||||
if err == nil {
|
||||
var shareLink xrayShareLink
|
||||
shareLink.text = proxy
|
||||
shareLink.link = link
|
||||
if outbound, err := shareLink.outbound(); err == nil {
|
||||
outbounds = append(outbounds, *outbound)
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(outbounds) == 0 {
|
||||
return nil, fmt.Errorf("no valid outbound found")
|
||||
}
|
||||
xray.Outbounds = outbounds
|
||||
return &xray, nil
|
||||
}
|
||||
|
||||
func tryParse(text string) (*xrayJson, error) {
|
||||
base64Text, err := decodeBase64Text(text)
|
||||
if err == nil {
|
||||
cleanText := checkWindowsReturn(base64Text)
|
||||
return parsePlainShareText(cleanText)
|
||||
}
|
||||
return tryConvertClashYaml(text)
|
||||
}
|
||||
|
||||
func decodeBase64Text(text string) (string, error) {
|
||||
content, err := base64.StdEncoding.DecodeString(text)
|
||||
if err == nil {
|
||||
return string(content), nil
|
||||
}
|
||||
if strings.Contains(text, "-") {
|
||||
text = strings.ReplaceAll(text, "-", "+")
|
||||
}
|
||||
if strings.Contains(text, "_") {
|
||||
text = strings.ReplaceAll(text, "_", "/")
|
||||
}
|
||||
missingPadding := len(text) % 4
|
||||
if missingPadding != 0 {
|
||||
padding := strings.Repeat("=", 4-missingPadding)
|
||||
text += padding
|
||||
}
|
||||
content, err = base64.StdEncoding.DecodeString(text)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func writeXrayJson(xray *xrayJson, xrayPath string) error {
|
||||
xrayBytes, err := json.Marshal(xray)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeBytes(xrayBytes, xrayPath)
|
||||
}
|
||||
|
||||
type xrayShareLink struct {
|
||||
text string
|
||||
link *url.URL
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) outbound() (*xrayOutbound, error) {
|
||||
switch proxy.link.Scheme {
|
||||
case "ss":
|
||||
outbound, err := proxy.shadowsocksOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
|
||||
case "vmess":
|
||||
outbound, err := proxy.vmessOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
|
||||
case "vless":
|
||||
outbound, err := proxy.vlessOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
|
||||
case "socks":
|
||||
outbound, err := proxy.socksOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
case "trojan":
|
||||
outbound, err := proxy.trojanOutbound()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupport link type: %s", proxy.link.Scheme)
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) shadowsocksOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "shadowsocks"
|
||||
outbound.Name = proxy.link.Fragment
|
||||
|
||||
var server xrayShadowsocksServer
|
||||
server.Address = proxy.link.Hostname()
|
||||
port, err := strconv.Atoi(proxy.link.Port())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server.Port = port
|
||||
|
||||
user := proxy.link.User.String()
|
||||
passwordText, err := decodeBase64Text(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pwConfig := strings.Split(passwordText, ":")
|
||||
if len(pwConfig) != 2 {
|
||||
return nil, fmt.Errorf("unsupport link shadowsocks password: %s", passwordText)
|
||||
}
|
||||
server.Method = pwConfig[0]
|
||||
server.Password = pwConfig[1]
|
||||
|
||||
var settings xrayShadowsocks
|
||||
settings.Servers = []xrayShadowsocksServer{server}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
outbound.StreamSettings = proxy.streamSettings(proxy.link.Query())
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) vmessOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "vmess"
|
||||
outbound.Name = proxy.link.Fragment
|
||||
|
||||
query := proxy.link.Query()
|
||||
|
||||
var user xrayVMessVnextUser
|
||||
user.Id = proxy.link.User.String()
|
||||
security := query.Get("encryption")
|
||||
if len(security) > 0 {
|
||||
user.Security = security
|
||||
}
|
||||
|
||||
var vnext xrayVMessVnext
|
||||
vnext.Address = proxy.link.Hostname()
|
||||
port, err := strconv.Atoi(proxy.link.Port())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vnext.Port = port
|
||||
vnext.Users = []xrayVMessVnextUser{user}
|
||||
|
||||
var settings xrayVMess
|
||||
settings.Vnext = []xrayVMessVnext{vnext}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
outbound.StreamSettings = proxy.streamSettings(query)
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) vlessOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "vless"
|
||||
outbound.Name = proxy.link.Fragment
|
||||
|
||||
query := proxy.link.Query()
|
||||
|
||||
var user xrayVLESSVnextUser
|
||||
user.Id = proxy.link.User.String()
|
||||
flow := query.Get("flow")
|
||||
if len(flow) > 0 {
|
||||
user.Flow = flow
|
||||
}
|
||||
|
||||
var vnext xrayVLESSVnext
|
||||
vnext.Address = proxy.link.Hostname()
|
||||
port, err := strconv.Atoi(proxy.link.Port())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vnext.Port = port
|
||||
vnext.Users = []xrayVLESSVnextUser{user}
|
||||
|
||||
var settings xrayVLESS
|
||||
settings.Vnext = []xrayVLESSVnext{vnext}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
outbound.StreamSettings = proxy.streamSettings(query)
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) socksOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "socks"
|
||||
outbound.Name = proxy.link.Fragment
|
||||
|
||||
userPassword := proxy.link.User.String()
|
||||
passwordText, err := decodeBase64Text(userPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pwConfig := strings.Split(passwordText, ":")
|
||||
if len(pwConfig) != 2 {
|
||||
return nil, fmt.Errorf("unsupport link socks user password: %s", passwordText)
|
||||
}
|
||||
var user xraySocksServerUser
|
||||
user.User = pwConfig[0]
|
||||
user.Pass = pwConfig[1]
|
||||
|
||||
var server xraySocksServer
|
||||
server.Address = proxy.link.Hostname()
|
||||
port, err := strconv.Atoi(proxy.link.Port())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server.Port = port
|
||||
server.Users = []xraySocksServerUser{user}
|
||||
|
||||
var settings xraySocks
|
||||
settings.Servers = []xraySocksServer{server}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
outbound.StreamSettings = proxy.streamSettings(proxy.link.Query())
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) trojanOutbound() (*xrayOutbound, error) {
|
||||
var outbound xrayOutbound
|
||||
outbound.Protocol = "trojan"
|
||||
outbound.Name = proxy.link.Fragment
|
||||
|
||||
var server xrayTrojanServer
|
||||
server.Address = proxy.link.Hostname()
|
||||
port, err := strconv.Atoi(proxy.link.Port())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server.Port = port
|
||||
server.Password = proxy.link.User.String()
|
||||
|
||||
var settings xrayTrojan
|
||||
settings.Servers = []xrayTrojanServer{server}
|
||||
|
||||
setttingsBytes, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.Settings = (*json.RawMessage)(&setttingsBytes)
|
||||
|
||||
outbound.StreamSettings = proxy.streamSettings(proxy.link.Query())
|
||||
|
||||
return &outbound, nil
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) streamSettings(query url.Values) *xrayStreamSettings {
|
||||
var streamSettings xrayStreamSettings
|
||||
if len(query) == 0 {
|
||||
return &streamSettings
|
||||
}
|
||||
network := query.Get("type")
|
||||
if len(network) == 0 {
|
||||
streamSettings.Network = "tcp"
|
||||
} else {
|
||||
streamSettings.Network = network
|
||||
}
|
||||
|
||||
switch streamSettings.Network {
|
||||
case "tcp":
|
||||
headerType := query.Get("headerType")
|
||||
if headerType == "http" {
|
||||
var request xrayTcpSettingsHeaderRequest
|
||||
path := query.Get("path")
|
||||
if len(path) > 0 {
|
||||
request.Path = strings.Split(path, ",")
|
||||
}
|
||||
host := query.Get("host")
|
||||
if len(host) > 0 {
|
||||
var headers xrayTcpSettingsHeaderRequestHeaders
|
||||
headers.Host = strings.Split(host, ",")
|
||||
request.Headers = &headers
|
||||
}
|
||||
var header xrayTcpSettingsHeader
|
||||
header.Type = headerType
|
||||
header.Request = &request
|
||||
|
||||
var tcpSettings xrayTcpSettings
|
||||
tcpSettings.Header = &header
|
||||
|
||||
streamSettings.TcpSettings = &tcpSettings
|
||||
}
|
||||
case "kcp":
|
||||
var kcpSettings xrayKcpSettings
|
||||
headerType := query.Get("headerType")
|
||||
if len(headerType) > 0 {
|
||||
var header xrayFakeHeader
|
||||
header.Type = headerType
|
||||
kcpSettings.Header = &header
|
||||
}
|
||||
seed := query.Get("seed")
|
||||
kcpSettings.Seed = seed
|
||||
|
||||
streamSettings.KcpSettings = &kcpSettings
|
||||
case "ws":
|
||||
var wsSettings xrayWsSettings
|
||||
path := query.Get("path")
|
||||
wsSettings.Path = path
|
||||
host := query.Get("host")
|
||||
if len(host) > 0 {
|
||||
var headers xrayWsSettingsHeaders
|
||||
headers.Host = host
|
||||
wsSettings.Headers = &headers
|
||||
}
|
||||
|
||||
streamSettings.WsSettings = &wsSettings
|
||||
case "grpc":
|
||||
var grcpSettings xrayGrpcSettings
|
||||
serviceName := query.Get("serviceName")
|
||||
grcpSettings.ServiceName = serviceName
|
||||
mode := query.Get("mode")
|
||||
grcpSettings.MultiMode = mode == "multi"
|
||||
|
||||
streamSettings.GrpcSettings = &grcpSettings
|
||||
case "quic":
|
||||
var quicSettings xrayQuicSettings
|
||||
headerType := query.Get("headerType")
|
||||
if len(headerType) > 0 {
|
||||
var header xrayFakeHeader
|
||||
header.Type = headerType
|
||||
quicSettings.Header = &header
|
||||
}
|
||||
quicSecurity := query.Get("quicSecurity")
|
||||
quicSettings.Security = quicSecurity
|
||||
key := query.Get("key")
|
||||
quicSettings.Key = key
|
||||
|
||||
streamSettings.QuicSettings = &quicSettings
|
||||
case "http":
|
||||
var httpSettings xrayHttpSettings
|
||||
host := query.Get("host")
|
||||
httpSettings.Host = strings.Split(host, ",")
|
||||
path := query.Get("path")
|
||||
httpSettings.Path = path
|
||||
|
||||
streamSettings.HttpSettings = &httpSettings
|
||||
}
|
||||
|
||||
proxy.parseSecurity(query, &streamSettings)
|
||||
|
||||
return &streamSettings
|
||||
}
|
||||
|
||||
func (proxy xrayShareLink) parseSecurity(query url.Values, streamSettings *xrayStreamSettings) {
|
||||
var tlsSettings xrayTlsSettings
|
||||
var realitySettings xrayRealitySettings
|
||||
|
||||
fp := query.Get("fp")
|
||||
tlsSettings.Fingerprint = fp
|
||||
realitySettings.Fingerprint = fp
|
||||
|
||||
sni := query.Get("sni")
|
||||
tlsSettings.ServerName = sni
|
||||
realitySettings.ServerName = sni
|
||||
|
||||
alpn := query.Get("alpn")
|
||||
if len(alpn) > 0 {
|
||||
tlsSettings.Alpn = strings.Split(alpn, ",")
|
||||
}
|
||||
|
||||
pbk := query.Get("pbk")
|
||||
realitySettings.PublicKey = pbk
|
||||
sid := query.Get("sid")
|
||||
realitySettings.ShortId = sid
|
||||
spx := query.Get("spx")
|
||||
realitySettings.SpiderX = spx
|
||||
|
||||
security := query.Get("security")
|
||||
if len(security) == 0 {
|
||||
streamSettings.Security = "none"
|
||||
} else {
|
||||
streamSettings.Security = security
|
||||
}
|
||||
|
||||
switch streamSettings.Security {
|
||||
case "tls":
|
||||
streamSettings.TlsSettings = &tlsSettings
|
||||
case "reality":
|
||||
streamSettings.RealitySettings = &realitySettings
|
||||
}
|
||||
}
|
||||
13
uuid.go
Normal file
13
uuid.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
)
|
||||
|
||||
func CustomUUID(str string) string {
|
||||
id, err := uuid.ParseString(str)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return id.String()
|
||||
}
|
||||
71
xray.go
Normal file
71
xray.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/xtls/xray-core/common/cmdarg"
|
||||
"github.com/xtls/xray-core/core"
|
||||
_ "github.com/xtls/xray-core/main/distro/all"
|
||||
)
|
||||
|
||||
var (
|
||||
coreServer *core.Instance
|
||||
)
|
||||
|
||||
func startXray(configFile string) (*core.Instance, error) {
|
||||
file := cmdarg.Arg{configFile}
|
||||
config, err := core.LoadConfig("json", file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
server, err := core.New(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func initEnv(datDir string) {
|
||||
os.Setenv("xray.location.asset", datDir)
|
||||
}
|
||||
|
||||
func setMaxMemory(maxMemory int64) {
|
||||
os.Setenv("XRAY_MEMORY_FORCEFREE", "1")
|
||||
initForceFree(maxMemory)
|
||||
}
|
||||
|
||||
func RunXray(datDir string, config string, maxMemory int64) string {
|
||||
initEnv(datDir)
|
||||
if maxMemory > 0 {
|
||||
setMaxMemory(maxMemory)
|
||||
}
|
||||
coreServer, err := startXray(config)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
if err := coreServer.Start(); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
debug.FreeOSMemory()
|
||||
return ""
|
||||
}
|
||||
|
||||
func StopXray() string {
|
||||
if coreServer != nil {
|
||||
err := coreServer.Close()
|
||||
coreServer = nil
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func XrayVersion() string {
|
||||
return core.Version()
|
||||
}
|
||||
162
xray_json.go
Normal file
162
xray_json.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package libxray
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type xrayJson struct {
|
||||
Outbounds []xrayOutbound `json:"outbounds,omitempty"`
|
||||
}
|
||||
|
||||
type xrayOutbound struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
Settings *json.RawMessage `json:"settings,omitempty"`
|
||||
StreamSettings *xrayStreamSettings `json:"streamSettings,omitempty"`
|
||||
}
|
||||
|
||||
type xrayShadowsocks struct {
|
||||
Servers []xrayShadowsocksServer `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
type xrayShadowsocksServer struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Port int `json:"port,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type xraySocks struct {
|
||||
Servers []xraySocksServer `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
type xraySocksServer struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Port int `json:"port,omitempty"`
|
||||
Users []xraySocksServerUser `json:"users,omitempty"`
|
||||
}
|
||||
|
||||
type xraySocksServerUser struct {
|
||||
User string `json:"user,omitempty"`
|
||||
Pass string `json:"pass,omitempty"`
|
||||
}
|
||||
|
||||
type xrayTrojan struct {
|
||||
Servers []xrayTrojanServer `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
type xrayTrojanServer struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Port int `json:"port,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type xrayVLESS struct {
|
||||
Vnext []xrayVLESSVnext `json:"vnext,omitempty"`
|
||||
}
|
||||
|
||||
type xrayVLESSVnext struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Port int `json:"port,omitempty"`
|
||||
Users []xrayVLESSVnextUser `json:"users,omitempty"`
|
||||
}
|
||||
|
||||
type xrayVLESSVnextUser struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Flow string `json:"flow,omitempty"`
|
||||
}
|
||||
|
||||
type xrayVMess struct {
|
||||
Vnext []xrayVMessVnext `json:"vnext,omitempty"`
|
||||
}
|
||||
|
||||
type xrayVMessVnext struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Port int `json:"port,omitempty"`
|
||||
Users []xrayVMessVnextUser `json:"users,omitempty"`
|
||||
}
|
||||
|
||||
type xrayVMessVnextUser struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Security string `json:"security,omitempty"`
|
||||
}
|
||||
|
||||
type xrayStreamSettings struct {
|
||||
Network string `json:"network,omitempty"`
|
||||
Security string `json:"security,omitempty"`
|
||||
TlsSettings *xrayTlsSettings `json:"tlsSettings,omitempty"`
|
||||
RealitySettings *xrayRealitySettings `json:"realitySettings,omitempty"`
|
||||
TcpSettings *xrayTcpSettings `json:"tcpSettings,omitempty"`
|
||||
KcpSettings *xrayKcpSettings `json:"kcpSettings,omitempty"`
|
||||
WsSettings *xrayWsSettings `json:"wsSettings,omitempty"`
|
||||
HttpSettings *xrayHttpSettings `json:"httpSettings,omitempty"`
|
||||
QuicSettings *xrayQuicSettings `json:"quicSettings,omitempty"`
|
||||
GrpcSettings *xrayGrpcSettings `json:"grpcSettings,omitempty"`
|
||||
}
|
||||
|
||||
type xrayTlsSettings struct {
|
||||
ServerName string `json:"serverName,omitempty"`
|
||||
AllowInsecure bool `json:"allowInsecure,omitempty"`
|
||||
Alpn []string `json:"alpn,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
}
|
||||
|
||||
type xrayRealitySettings struct {
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
ServerName string `json:"serverName,omitempty"`
|
||||
PublicKey string `json:"publicKey,omitempty"`
|
||||
ShortId string `json:"shortId,omitempty"`
|
||||
SpiderX string `json:"spiderX,omitempty"`
|
||||
}
|
||||
|
||||
type xrayTcpSettings struct {
|
||||
Header *xrayTcpSettingsHeader `json:"header,omitempty"`
|
||||
}
|
||||
|
||||
type xrayTcpSettingsHeader struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Request *xrayTcpSettingsHeaderRequest `json:"request,omitempty"`
|
||||
}
|
||||
|
||||
type xrayTcpSettingsHeaderRequest struct {
|
||||
Path []string `json:"path,omitempty"`
|
||||
Headers *xrayTcpSettingsHeaderRequestHeaders `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
type xrayTcpSettingsHeaderRequestHeaders struct {
|
||||
Host []string `json:"Host,omitempty"`
|
||||
}
|
||||
|
||||
type xrayFakeHeader struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type xrayKcpSettings struct {
|
||||
Header *xrayFakeHeader `json:"header,omitempty"`
|
||||
Seed string `json:"seed,omitempty"`
|
||||
}
|
||||
|
||||
type xrayWsSettings struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers *xrayWsSettingsHeaders `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
type xrayWsSettingsHeaders struct {
|
||||
Host string `json:"Host,omitempty"`
|
||||
}
|
||||
|
||||
type xrayHttpSettings struct {
|
||||
Host []string `json:"host,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
type xrayQuicSettings struct {
|
||||
Security string `json:"security,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Header *xrayFakeHeader `json:"header,omitempty"`
|
||||
}
|
||||
|
||||
type xrayGrpcSettings struct {
|
||||
ServiceName string `json:"serviceName,omitempty"`
|
||||
MultiMode bool `json:"multiMode,omitempty"`
|
||||
}
|
||||
Reference in New Issue
Block a user