add
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package util
|
||||
|
||||
import "encoding/base64"
|
||||
|
||||
// Function to return decoded bytes if a string is Base64 encoded
|
||||
func StrOrBase64Encoded(str string) string {
|
||||
decoded, err := base64.StdEncoding.DecodeString(str)
|
||||
if err == nil {
|
||||
return string(decoded)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func B64StrToByte(str string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(str)
|
||||
}
|
||||
|
||||
func ByteToB64Str(b []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package common
|
||||
|
||||
// UnionUintArray returns a new unique slice that contains all elements from both input slices
|
||||
func UnionUintArray(a []uint, b []uint) []uint {
|
||||
m := make(map[uint]bool)
|
||||
for _, v := range a {
|
||||
m[v] = true
|
||||
}
|
||||
for _, v := range b {
|
||||
m[v] = true
|
||||
}
|
||||
var res []uint
|
||||
for k := range m {
|
||||
res = append(res, k)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Find different elements in two slices
|
||||
// Returns elements in 'a' that are not in 'b' and elements in 'b' that are not in 'a'
|
||||
func DiffUintArray(a []uint, b []uint) []uint {
|
||||
different := []uint{}
|
||||
set := make(map[uint]bool)
|
||||
|
||||
for _, item := range a {
|
||||
set[item] = true
|
||||
}
|
||||
for _, item := range b {
|
||||
if !set[item] {
|
||||
different = append(different, item)
|
||||
}
|
||||
}
|
||||
|
||||
set = make(map[uint]bool)
|
||||
for _, item := range b {
|
||||
set[item] = true
|
||||
}
|
||||
for _, item := range a {
|
||||
if !set[item] {
|
||||
different = append(different, item)
|
||||
}
|
||||
}
|
||||
|
||||
return different
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/alireza0/s-ui/logger"
|
||||
)
|
||||
|
||||
func NewErrorf(format string, a ...interface{}) error {
|
||||
msg := fmt.Sprintf(format, a...)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
func NewError(a ...interface{}) error {
|
||||
msg := fmt.Sprintln(a...)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
func Recover(msg string) interface{} {
|
||||
panicErr := recover()
|
||||
if panicErr != nil {
|
||||
if msg != "" {
|
||||
logger.Error(msg, "panic:", panicErr)
|
||||
}
|
||||
}
|
||||
return panicErr
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
allSeq []rune = []rune{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
}
|
||||
|
||||
fallbackRand = mrand.New(mrand.NewSource(time.Now().UnixNano()))
|
||||
fallbackMu = sync.Mutex{}
|
||||
)
|
||||
|
||||
func Random(n int) string {
|
||||
if n <= 0 || len(allSeq) == 0 {
|
||||
return ""
|
||||
}
|
||||
result := make([]rune, n)
|
||||
maxBig := big.NewInt(int64(len(allSeq)))
|
||||
for i := 0; i < n; i++ {
|
||||
num, err := crand.Int(crand.Reader, maxBig)
|
||||
if err != nil {
|
||||
// fallback
|
||||
fallbackMu.Lock()
|
||||
result[i] = allSeq[fallbackRand.Intn(len(allSeq))]
|
||||
fallbackMu.Unlock()
|
||||
continue
|
||||
}
|
||||
result[i] = allSeq[int(num.Int64())]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func RandomInt(n int) int {
|
||||
if n <= 0 {
|
||||
return 0
|
||||
}
|
||||
max := big.NewInt(int64(n))
|
||||
result, err := crand.Int(crand.Reader, max)
|
||||
if err != nil {
|
||||
// fallback
|
||||
fallbackMu.Lock()
|
||||
defer fallbackMu.Unlock()
|
||||
return fallbackRand.Intn(n)
|
||||
}
|
||||
return int(result.Int64())
|
||||
}
|
||||
+615
@@ -0,0 +1,615 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/alireza0/s-ui/database/model"
|
||||
"github.com/alireza0/s-ui/util/common"
|
||||
)
|
||||
|
||||
var InboundTypeWithLink = []string{"socks", "http", "mixed", "shadowsocks", "naive", "hysteria", "hysteria2", "anytls", "tuic", "vless", "trojan", "vmess"}
|
||||
|
||||
type LinkParam struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname string) []string {
|
||||
inbound, err := i.MarshalFull()
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
var tls map[string]interface{}
|
||||
if i.TlsId > 0 {
|
||||
tls = prepareTls(i.Tls)
|
||||
}
|
||||
|
||||
var userConfig map[string]map[string]interface{}
|
||||
if err := json.Unmarshal(clientConfig, &userConfig); err != nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
var Addrs []map[string]interface{}
|
||||
if err := json.Unmarshal(i.Addrs, &Addrs); err != nil {
|
||||
return []string{}
|
||||
}
|
||||
if len(Addrs) == 0 {
|
||||
Addrs = append(Addrs, map[string]interface{}{
|
||||
"server": hostname,
|
||||
"server_port": (*inbound)["listen_port"],
|
||||
"remark": i.Tag,
|
||||
})
|
||||
if i.TlsId > 0 {
|
||||
Addrs[0]["tls"] = tls
|
||||
}
|
||||
} else {
|
||||
for index, addr := range Addrs {
|
||||
addrRemark, _ := addr["remark"].(string)
|
||||
Addrs[index]["remark"] = i.Tag + addrRemark
|
||||
if i.TlsId > 0 {
|
||||
newTls := map[string]interface{}{}
|
||||
for k, v := range tls {
|
||||
newTls[k] = v
|
||||
}
|
||||
|
||||
// Override tls
|
||||
if addrTls, ok := addr["tls"].(map[string]interface{}); ok {
|
||||
for k, v := range addrTls {
|
||||
newTls[k] = v
|
||||
}
|
||||
}
|
||||
Addrs[index]["tls"] = newTls
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch i.Type {
|
||||
case "socks":
|
||||
return socksLink(userConfig["socks"], Addrs)
|
||||
case "http":
|
||||
return httpLink(userConfig["http"], Addrs)
|
||||
case "mixed":
|
||||
return append(
|
||||
socksLink(userConfig["socks"], Addrs),
|
||||
httpLink(userConfig["http"], Addrs)...,
|
||||
)
|
||||
case "shadowsocks":
|
||||
return shadowsocksLink(userConfig, *inbound, Addrs)
|
||||
case "naive":
|
||||
return naiveLink(userConfig["naive"], *inbound, Addrs)
|
||||
case "hysteria":
|
||||
return hysteriaLink(userConfig["hysteria"], *inbound, Addrs)
|
||||
case "hysteria2":
|
||||
return hysteria2Link(userConfig["hysteria2"], *inbound, Addrs)
|
||||
case "tuic":
|
||||
return tuicLink(userConfig["tuic"], *inbound, Addrs)
|
||||
case "vless":
|
||||
return vlessLink(userConfig["vless"], *inbound, Addrs)
|
||||
case "anytls":
|
||||
return anytlsLink(userConfig["anytls"], Addrs)
|
||||
case "trojan":
|
||||
return trojanLink(userConfig["trojan"], *inbound, Addrs)
|
||||
case "vmess":
|
||||
return vmessLink(userConfig["vmess"], *inbound, Addrs)
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func prepareTls(t *model.Tls) map[string]interface{} {
|
||||
var iTls, oTls map[string]interface{}
|
||||
if err := json.Unmarshal(t.Client, &oTls); err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := json.Unmarshal(t.Server, &iTls); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range iTls {
|
||||
switch k {
|
||||
case "enabled", "server_name", "alpn":
|
||||
oTls[k] = v
|
||||
case "reality":
|
||||
reality := v.(map[string]interface{})
|
||||
clientReality := oTls["reality"].(map[string]interface{})
|
||||
clientReality["enabled"] = reality["enabled"]
|
||||
if shortIDs, hasSIds := reality["short_id"].([]interface{}); hasSIds && len(shortIDs) > 0 {
|
||||
clientReality["short_id"] = shortIDs[common.RandomInt(len(shortIDs))]
|
||||
}
|
||||
oTls["reality"] = clientReality
|
||||
}
|
||||
}
|
||||
return oTls
|
||||
}
|
||||
|
||||
func socksLink(userConfig map[string]interface{}, addrs []map[string]interface{}) []string {
|
||||
var links []string
|
||||
for _, addr := range addrs {
|
||||
links = append(links, fmt.Sprintf("socks5://%s:%s@%s:%d", userConfig["username"], userConfig["password"], addr["server"].(string), uint(addr["server_port"].(float64))))
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
func httpLink(userConfig map[string]interface{}, addrs []map[string]interface{}) []string {
|
||||
var links []string
|
||||
protocol := "http"
|
||||
for _, addr := range addrs {
|
||||
if addr["tls"] != nil {
|
||||
protocol = "https"
|
||||
}
|
||||
links = append(links, fmt.Sprintf("%s://%s:%s@%s:%d", protocol, userConfig["username"], userConfig["password"], addr["server"].(string), uint(addr["server_port"].(float64))))
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
func shadowsocksLink(
|
||||
userConfig map[string]map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
var userPass []string
|
||||
method, _ := inbound["method"].(string)
|
||||
if strings.HasPrefix(method, "2022") {
|
||||
inbPass, _ := inbound["password"].(string)
|
||||
userPass = append(userPass, inbPass)
|
||||
}
|
||||
var pass string
|
||||
if method == "2022-blake3-aes-128-gcm" {
|
||||
pass, _ = userConfig["shadowsocks16"]["password"].(string)
|
||||
} else {
|
||||
pass, _ = userConfig["shadowsocks"]["password"].(string)
|
||||
}
|
||||
userPass = append(userPass, pass)
|
||||
|
||||
uriBase := fmt.Sprintf("ss://%s", toBase64([]byte(fmt.Sprintf("%s:%s", method, strings.Join(userPass, ":")))))
|
||||
|
||||
var links []string
|
||||
for _, addr := range addrs {
|
||||
port, _ := addr["server_port"].(float64)
|
||||
links = append(links, fmt.Sprintf("%s@%s:%.0f#%s", uriBase, addr["server"].(string), port, addr["remark"].(string)))
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
func naiveLink(
|
||||
userConfig map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
password, _ := userConfig["password"].(string)
|
||||
username, _ := userConfig["username"].(string)
|
||||
|
||||
baseUri := "http2://"
|
||||
var links []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
var params []LinkParam
|
||||
params = append(params, LinkParam{"padding", "1"})
|
||||
if tls, ok := addr["tls"].(map[string]interface{}); ok {
|
||||
if sni, ok := tls["server_name"].(string); ok {
|
||||
params = append(params, LinkParam{"peer", sni})
|
||||
}
|
||||
if alpn, ok := tls["alpn"].([]interface{}); ok {
|
||||
alpnList := make([]string, len(alpn))
|
||||
for i, v := range alpn {
|
||||
alpnList[i] = v.(string)
|
||||
}
|
||||
params = append(params, LinkParam{"alpn", strings.Join(alpnList, ",")})
|
||||
}
|
||||
if insecure, ok := tls["insecure"].(bool); ok && insecure {
|
||||
params = append(params, LinkParam{"insecure", "1"})
|
||||
}
|
||||
}
|
||||
if tfo, ok := inbound["tcp_fast_open"].(bool); ok && tfo {
|
||||
params = append(params, LinkParam{"tfo", "1"})
|
||||
} else {
|
||||
params = append(params, LinkParam{"tfo", "0"})
|
||||
}
|
||||
|
||||
port, _ := addr["server_port"].(float64)
|
||||
uri := baseUri + toBase64([]byte(fmt.Sprintf("%s:%s@%s:%.0f", username, password, addr["server"].(string), port)))
|
||||
links = append(links, addParams(uri, params, addr["remark"].(string)))
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
func hysteriaLink(
|
||||
userConfig map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
baseUri := "hysteria://"
|
||||
var links []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
var params []LinkParam
|
||||
if upmbps, ok := inbound["up_mbps"].(float64); ok {
|
||||
params = append(params, LinkParam{"downmbps", fmt.Sprintf("%.0f", upmbps)})
|
||||
}
|
||||
if downmbps, ok := inbound["down_mbps"].(float64); ok {
|
||||
params = append(params, LinkParam{"upmbps", fmt.Sprintf("%.0f", downmbps)})
|
||||
}
|
||||
if auth, ok := userConfig["auth_str"].(string); ok {
|
||||
params = append(params, LinkParam{"auth", auth})
|
||||
}
|
||||
if tls, ok := addr["tls"].(map[string]interface{}); ok {
|
||||
getTlsParams(¶ms, tls, "insecure")
|
||||
}
|
||||
if obfs, ok := inbound["obfs"].(string); ok {
|
||||
params = append(params, LinkParam{"obfs", obfs})
|
||||
}
|
||||
if tfo, ok := inbound["tcp_fast_open"].(bool); ok && tfo {
|
||||
params = append(params, LinkParam{"fastopen", "1"})
|
||||
} else {
|
||||
params = append(params, LinkParam{"fastopen", "0"})
|
||||
}
|
||||
var outJson map[string]interface{}
|
||||
if err := json.Unmarshal(inbound["out_json"].(json.RawMessage), &outJson); err != nil {
|
||||
return []string{} // Handle error
|
||||
}
|
||||
if mport, ok := outJson["server_ports"].([]interface{}); ok {
|
||||
mportList := make([]string, len(mport))
|
||||
for i, v := range mport {
|
||||
mportList[i] = v.(string)
|
||||
}
|
||||
params = append(params, LinkParam{"mport", strings.Join(mportList, ",")})
|
||||
}
|
||||
|
||||
port, _ := addr["server_port"].(float64)
|
||||
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
|
||||
links = append(links, addParams(uri, params, addr["remark"].(string)))
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
func hysteria2Link(
|
||||
userConfig map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
password, _ := userConfig["password"].(string)
|
||||
baseUri := fmt.Sprintf("%s%s@", "hysteria2://", password)
|
||||
var links []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
var params []LinkParam
|
||||
if upmbps, ok := inbound["up_mbps"].(float64); ok {
|
||||
params = append(params, LinkParam{"downmbps", fmt.Sprintf("%.0f", upmbps)})
|
||||
}
|
||||
if downmbps, ok := inbound["down_mbps"].(float64); ok {
|
||||
params = append(params, LinkParam{"upmbps", fmt.Sprintf("%.0f", downmbps)})
|
||||
}
|
||||
if tls, ok := addr["tls"].(map[string]interface{}); ok {
|
||||
getTlsParams(¶ms, tls, "insecure")
|
||||
}
|
||||
if obfs, ok := inbound["obfs"].(map[string]interface{}); ok {
|
||||
if obfsType, ok := obfs["type"].(string); ok {
|
||||
params = append(params, LinkParam{"obfs", obfsType})
|
||||
}
|
||||
if obfsPassword, ok := obfs["password"].(string); ok {
|
||||
params = append(params, LinkParam{"obfs-password", obfsPassword})
|
||||
}
|
||||
}
|
||||
if tfo, ok := inbound["tcp_fast_open"].(bool); ok && tfo {
|
||||
params = append(params, LinkParam{"fastopen", "1"})
|
||||
} else {
|
||||
params = append(params, LinkParam{"fastopen", "0"})
|
||||
}
|
||||
var outJson map[string]interface{}
|
||||
if err := json.Unmarshal(inbound["out_json"].(json.RawMessage), &outJson); err != nil {
|
||||
return []string{} // Handle error
|
||||
}
|
||||
if mport, ok := outJson["server_ports"].([]interface{}); ok {
|
||||
mportList := make([]string, len(mport))
|
||||
for i, v := range mport {
|
||||
mportList[i] = v.(string)
|
||||
}
|
||||
params = append(params, LinkParam{"mport", strings.Join(mportList, ",")})
|
||||
}
|
||||
|
||||
port, _ := addr["server_port"].(float64)
|
||||
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
|
||||
links = append(links, addParams(uri, params, addr["remark"].(string)))
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
func anytlsLink(
|
||||
userConfig map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
password, _ := userConfig["password"].(string)
|
||||
baseUri := fmt.Sprintf("%s%s@", "anytls://", password)
|
||||
var links []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
var params []LinkParam
|
||||
if tls, ok := addr["tls"].(map[string]interface{}); ok {
|
||||
getTlsParams(¶ms, tls, "insecure")
|
||||
}
|
||||
|
||||
port, _ := addr["server_port"].(float64)
|
||||
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
|
||||
links = append(links, addParams(uri, params, addr["remark"].(string)))
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
func tuicLink(
|
||||
userConfig map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
password, _ := userConfig["password"].(string)
|
||||
uuid, _ := userConfig["uuid"].(string)
|
||||
baseUri := fmt.Sprintf("%s%s:%s@", "tuic://", uuid, password)
|
||||
var links []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
var params []LinkParam
|
||||
if tls, ok := addr["tls"].(map[string]interface{}); ok {
|
||||
getTlsParams(¶ms, tls, "insecure")
|
||||
}
|
||||
if congestionControl, ok := inbound["congestion_control"].(string); ok {
|
||||
params = append(params, LinkParam{"congestion_control", congestionControl})
|
||||
}
|
||||
|
||||
port, _ := addr["server_port"].(float64)
|
||||
uri := fmt.Sprintf("%s%s:%.0f", baseUri, addr["server"].(string), port)
|
||||
links = append(links, addParams(uri, params, addr["remark"].(string)))
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
func vlessLink(
|
||||
userConfig map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
uuid, _ := userConfig["uuid"].(string)
|
||||
baseParams := getTransportParams(inbound["transport"])
|
||||
var links []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
params := make([]LinkParam, len(baseParams))
|
||||
copy(params, baseParams)
|
||||
if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) {
|
||||
getTlsParams(¶ms, tls, "allowInsecure")
|
||||
if flow, ok := userConfig["flow"].(string); ok {
|
||||
params = append(params, LinkParam{"flow", flow})
|
||||
}
|
||||
}
|
||||
port, _ := addr["server_port"].(float64)
|
||||
uri := fmt.Sprintf("vless://%s@%s:%.0f", uuid, addr["server"].(string), port)
|
||||
uri = addParams(uri, params, addr["remark"].(string))
|
||||
links = append(links, uri)
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
func trojanLink(
|
||||
userConfig map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
password, _ := userConfig["password"].(string)
|
||||
baseParams := getTransportParams(inbound["transport"])
|
||||
var links []string
|
||||
|
||||
for _, addr := range addrs {
|
||||
params := make([]LinkParam, len(baseParams))
|
||||
copy(params, baseParams)
|
||||
if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) {
|
||||
getTlsParams(¶ms, tls, "allowInsecure")
|
||||
}
|
||||
port, _ := addr["server_port"].(float64)
|
||||
uri := fmt.Sprintf("trojan://%s@%s:%.0f", password, addr["server"].(string), port)
|
||||
uri = addParams(uri, params, addr["remark"].(string))
|
||||
links = append(links, uri)
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
func vmessLink(
|
||||
userConfig map[string]interface{},
|
||||
inbound map[string]interface{},
|
||||
addrs []map[string]interface{}) []string {
|
||||
|
||||
uuid, _ := userConfig["uuid"].(string)
|
||||
transportParams := getTransportParams(inbound["transport"])
|
||||
var links []string
|
||||
|
||||
baseParams := map[string]interface{}{
|
||||
"v": "2",
|
||||
"id": uuid,
|
||||
"aid": 0,
|
||||
}
|
||||
|
||||
var net, typ, host, path string
|
||||
for _, p := range transportParams {
|
||||
switch p.Key {
|
||||
case "type":
|
||||
net = p.Value
|
||||
case "host":
|
||||
host = p.Value
|
||||
case "path":
|
||||
path = p.Value
|
||||
}
|
||||
}
|
||||
|
||||
if net == "http" || net == "tcp" {
|
||||
baseParams["net"] = "tcp"
|
||||
if net == "http" {
|
||||
typ = "http"
|
||||
}
|
||||
} else {
|
||||
baseParams["net"] = net
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
obj := make(map[string]interface{})
|
||||
for k, v := range baseParams {
|
||||
obj[k] = v
|
||||
}
|
||||
|
||||
obj["add"], _ = addr["server"].(string)
|
||||
port, _ := addr["server_port"].(float64)
|
||||
obj["port"] = fmt.Sprintf("%.0f", port)
|
||||
obj["ps"], _ = addr["remark"].(string)
|
||||
if typ != "" {
|
||||
obj["type"] = typ
|
||||
}
|
||||
if host != "" {
|
||||
obj["host"] = host
|
||||
}
|
||||
if path != "" {
|
||||
obj["path"] = path
|
||||
}
|
||||
populateVmessTlsParams(obj, addr["tls"])
|
||||
|
||||
jsonStr, _ := json.Marshal(obj)
|
||||
|
||||
uri := fmt.Sprintf("vmess://%s", toBase64(jsonStr))
|
||||
links = append(links, uri)
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
func populateVmessTlsParams(obj map[string]interface{}, tlsConfig interface{}) {
|
||||
if tlsMap, ok := tlsConfig.(map[string]interface{}); ok && tlsMap["enabled"].(bool) {
|
||||
obj["tls"] = "tls"
|
||||
var tlsParams []LinkParam
|
||||
getTlsParams(&tlsParams, tlsMap, "allowInsecure")
|
||||
for _, p := range tlsParams {
|
||||
switch p.Key {
|
||||
case "security":
|
||||
// ignore, as "tls" is already set
|
||||
case "allowInsecure":
|
||||
obj["allowInsecure"] = 1
|
||||
case "sni":
|
||||
obj["sni"] = p.Value
|
||||
case "fp":
|
||||
obj["fp"] = p.Value
|
||||
case "alpn":
|
||||
obj["alpn"] = p.Value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obj["tls"] = "none"
|
||||
}
|
||||
}
|
||||
|
||||
func toBase64(d []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(d)
|
||||
}
|
||||
|
||||
func addParams(uri string, params []LinkParam, remark string) string {
|
||||
URL, _ := url.Parse(uri)
|
||||
var q []string
|
||||
for _, p := range params {
|
||||
switch p.Key {
|
||||
case "mport", "alpn":
|
||||
q = append(q, fmt.Sprintf("%s=%s", p.Key, p.Value))
|
||||
default:
|
||||
q = append(q, fmt.Sprintf("%s=%s", p.Key, url.QueryEscape(p.Value)))
|
||||
}
|
||||
}
|
||||
URL.RawQuery = strings.Join(q, "&")
|
||||
URL.Fragment = remark
|
||||
return URL.String()
|
||||
}
|
||||
|
||||
func getTransportParams(t interface{}) []LinkParam {
|
||||
var params []LinkParam
|
||||
trasport, _ := t.(map[string]interface{})
|
||||
var transportType string
|
||||
if tt, ok := trasport["type"].(string); ok {
|
||||
transportType = tt
|
||||
} else {
|
||||
transportType = "tcp"
|
||||
}
|
||||
params = append(params, LinkParam{"type", transportType})
|
||||
if transportType == "tcp" {
|
||||
return params
|
||||
}
|
||||
|
||||
switch transportType {
|
||||
case "http":
|
||||
if host, ok := trasport["host"].([]interface{}); ok {
|
||||
var hosts []string
|
||||
for _, v := range host {
|
||||
hosts = append(hosts, v.(string))
|
||||
}
|
||||
params = append(params, LinkParam{"host", strings.Join(hosts, ",")})
|
||||
}
|
||||
if path, ok := trasport["path"].(string); ok {
|
||||
params = append(params, LinkParam{"path", path})
|
||||
}
|
||||
case "ws":
|
||||
if path, ok := trasport["path"].(string); ok {
|
||||
params = append(params, LinkParam{"path", path})
|
||||
}
|
||||
if headers, ok := trasport["headers"].(map[string]interface{}); ok {
|
||||
if host, ok := headers["Host"].(string); ok {
|
||||
params = append(params, LinkParam{"host", host})
|
||||
}
|
||||
}
|
||||
case "grpc":
|
||||
if serviceName, ok := trasport["service_name"].(string); ok {
|
||||
params = append(params, LinkParam{"serviceName", serviceName})
|
||||
}
|
||||
case "httpupgrade":
|
||||
if host, ok := trasport["host"].(string); ok {
|
||||
params = append(params, LinkParam{"host", host})
|
||||
}
|
||||
if path, ok := trasport["path"].(string); ok {
|
||||
params = append(params, LinkParam{"path", path})
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func getTlsParams(params *[]LinkParam, tls map[string]interface{}, insecureKey string) {
|
||||
if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) {
|
||||
*params = append(*params, LinkParam{"security", "reality"})
|
||||
if pbk, ok := reality["public_key"].(string); ok {
|
||||
*params = append(*params, LinkParam{"pbk", pbk})
|
||||
}
|
||||
if sid, ok := reality["short_id"].(string); ok {
|
||||
*params = append(*params, LinkParam{"sid", sid})
|
||||
}
|
||||
} else {
|
||||
*params = append(*params, LinkParam{"security", "tls"})
|
||||
if insecure, ok := tls["insecure"].(bool); ok && insecure {
|
||||
*params = append(*params, LinkParam{insecureKey, "1"})
|
||||
}
|
||||
if disableSni, ok := tls["disable_sni"].(bool); ok && disableSni {
|
||||
*params = append(*params, LinkParam{"disable_sni", "1"})
|
||||
}
|
||||
}
|
||||
if utls, ok := tls["utls"].(map[string]interface{}); ok {
|
||||
if fingerprint, ok := utls["fingerprint"].(string); ok {
|
||||
*params = append(*params, LinkParam{"fp", fingerprint})
|
||||
}
|
||||
}
|
||||
if sni, ok := tls["server_name"].(string); ok {
|
||||
*params = append(*params, LinkParam{"sni", sni})
|
||||
}
|
||||
if alpn, ok := tls["alpn"].([]interface{}); ok {
|
||||
alpnList := make([]string, len(alpn))
|
||||
for i, v := range alpn {
|
||||
alpnList[i] = v.(string)
|
||||
}
|
||||
*params = append(*params, LinkParam{"alpn", strings.Join(alpnList, ",")})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alireza0/s-ui/util/common"
|
||||
)
|
||||
|
||||
func GetOutbound(uri string, i int) (*map[string]interface{}, string, error) {
|
||||
u, err := url.Parse(uri)
|
||||
if err == nil {
|
||||
switch u.Scheme {
|
||||
case "vmess":
|
||||
return vmess(u.Host, i)
|
||||
case "vless":
|
||||
return vless(u, i)
|
||||
case "trojan":
|
||||
return trojan(u, i)
|
||||
case "hy", "hysteria":
|
||||
return hy(u, i)
|
||||
case "hy2", "hysteria2":
|
||||
return hy2(u, i)
|
||||
case "anytls":
|
||||
return anytls(u, i)
|
||||
case "tuic":
|
||||
return tuic(u, i)
|
||||
case "ss", "shadowsocks":
|
||||
return ss(u, i)
|
||||
case "naive+https", "naive+quic", "http2":
|
||||
return parseNaiveLink(u, i)
|
||||
}
|
||||
}
|
||||
return nil, "", common.NewError("Unsupported link format")
|
||||
}
|
||||
|
||||
func vmess(data string, i int) (*map[string]interface{}, string, error) {
|
||||
dataByte, err := B64StrToByte(data)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var dataJson map[string]interface{}
|
||||
err = json.Unmarshal(dataByte, &dataJson)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
transport := map[string]interface{}{}
|
||||
tp_net, _ := dataJson["net"].(string)
|
||||
tp_type, _ := dataJson["type"].(string)
|
||||
tp_host, _ := dataJson["host"].(string)
|
||||
tp_path, _ := dataJson["path"].(string)
|
||||
switch strings.ToLower(tp_net) {
|
||||
case "tcp", "":
|
||||
if tp_type == "http" {
|
||||
transport["type"] = tp_type
|
||||
if len(tp_host) > 0 {
|
||||
transport["host"] = strings.Split(tp_host, ",")
|
||||
}
|
||||
transport["path"] = tp_path
|
||||
}
|
||||
case "http", "h2":
|
||||
transport["type"] = "http"
|
||||
if len(tp_host) > 0 {
|
||||
transport["host"] = strings.Split(tp_host, ",")
|
||||
}
|
||||
transport["path"] = tp_path
|
||||
case "ws":
|
||||
transport["type"] = tp_net
|
||||
transport["path"] = tp_path
|
||||
transport["early_data_header_name"] = "Sec-WebSocket-Protocol"
|
||||
if len(tp_host) > 0 {
|
||||
transport["headers"] = map[string]interface{}{
|
||||
"Host": tp_host,
|
||||
}
|
||||
}
|
||||
case "quic":
|
||||
transport["type"] = tp_net
|
||||
case "grpc":
|
||||
transport["type"] = tp_net
|
||||
transport["service_name"] = tp_path
|
||||
case "httpupgrade":
|
||||
transport["type"] = tp_net
|
||||
transport["path"] = tp_path
|
||||
transport["host"] = tp_host
|
||||
default:
|
||||
return nil, "", common.NewError("Invalid vmess")
|
||||
}
|
||||
tls := map[string]interface{}{}
|
||||
vmess_tls, _ := dataJson["tls"].(string)
|
||||
if vmess_tls == "tls" {
|
||||
tls["enabled"] = true
|
||||
tls_sni, _ := dataJson["sni"].(string)
|
||||
tls_alpn, _ := dataJson["alpn"].(string)
|
||||
_, tls_insecure := dataJson["allowInsecure"]
|
||||
tls_fp, _ := dataJson["fp"].(string)
|
||||
if len(tls_sni) > 0 {
|
||||
tls["server_name"] = tls_sni
|
||||
}
|
||||
if len(tls_alpn) > 0 {
|
||||
tls["alpn"] = strings.Split(tls_alpn, ",")
|
||||
}
|
||||
if tls_insecure {
|
||||
tls["insecure"] = true
|
||||
}
|
||||
if len(tls_fp) > 0 {
|
||||
tls["utls"] = map[string]interface{}{
|
||||
"enabled": true,
|
||||
"fingerprint": tls_fp,
|
||||
}
|
||||
}
|
||||
}
|
||||
tag, _ := dataJson["ps"].(string)
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, tag)
|
||||
}
|
||||
alter_id := 0
|
||||
if aid, ok := dataJson["aid"].(float64); ok {
|
||||
alter_id = int(aid)
|
||||
}
|
||||
vmess := map[string]interface{}{
|
||||
"type": "vmess",
|
||||
"tag": tag,
|
||||
"server": dataJson["add"],
|
||||
"server_port": dataJson["port"],
|
||||
"uuid": dataJson["id"],
|
||||
"security": "auto",
|
||||
"alter_id": alter_id,
|
||||
"tls": tls,
|
||||
"transport": transport,
|
||||
}
|
||||
return &vmess, tag, err
|
||||
}
|
||||
|
||||
func vless(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
query, _ := url.ParseQuery(u.RawQuery)
|
||||
security := query.Get("security")
|
||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||
port := 80
|
||||
if len(portStr) > 0 {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
} else {
|
||||
if security == "tls" || security == "reality" {
|
||||
port = 443
|
||||
}
|
||||
}
|
||||
tp_type := query.Get("type")
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
vless := map[string]interface{}{
|
||||
"type": "vless",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"uuid": u.User.Username(),
|
||||
"flow": query.Get("flow"),
|
||||
"tls": getTls(security, &query),
|
||||
"transport": getTransport(tp_type, &query),
|
||||
}
|
||||
return &vless, tag, nil
|
||||
}
|
||||
|
||||
func trojan(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
query, _ := url.ParseQuery(u.RawQuery)
|
||||
security := query.Get("security")
|
||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||
port := 80
|
||||
if len(portStr) > 0 {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
} else {
|
||||
if security == "tls" || security == "reality" {
|
||||
port = 443
|
||||
}
|
||||
}
|
||||
tp_type := query.Get("type")
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
trojan := map[string]interface{}{
|
||||
"type": "trojan",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"password": u.User.Username(),
|
||||
"tls": getTls(security, &query),
|
||||
"transport": getTransport(tp_type, &query),
|
||||
}
|
||||
return &trojan, tag, nil
|
||||
}
|
||||
|
||||
func hy(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
query, _ := url.ParseQuery(u.RawQuery)
|
||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||
port := 443
|
||||
if len(portStr) > 0 {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
}
|
||||
|
||||
security := query.Get("security")
|
||||
if len(security) == 0 {
|
||||
security = "tls"
|
||||
}
|
||||
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
hy := map[string]interface{}{
|
||||
"type": "hysteria",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"obfs": query.Get("obfsParam"),
|
||||
"auth_str": query.Get("auth"),
|
||||
"tls": getTls(security, &query),
|
||||
}
|
||||
down, _ := strconv.Atoi(query.Get("downmbps"))
|
||||
up, _ := strconv.Atoi(query.Get("upmbps"))
|
||||
recv_window_conn, _ := strconv.Atoi(query.Get("recv_window_conn"))
|
||||
recv_window, _ := strconv.Atoi(query.Get("recv_window"))
|
||||
if down > 0 {
|
||||
hy["down_mbps"] = down
|
||||
}
|
||||
if up > 0 {
|
||||
hy["up_mbps"] = up
|
||||
}
|
||||
if recv_window_conn > 0 {
|
||||
hy["recv_window_conn"] = recv_window_conn
|
||||
}
|
||||
if recv_window > 0 {
|
||||
hy["recv_window"] = recv_window
|
||||
}
|
||||
return &hy, tag, nil
|
||||
}
|
||||
|
||||
func hy2(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
query, _ := url.ParseQuery(u.RawQuery)
|
||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||
port := 443
|
||||
if len(portStr) > 0 {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
}
|
||||
|
||||
security := query.Get("security")
|
||||
if len(security) == 0 {
|
||||
security = "tls"
|
||||
}
|
||||
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
hy2 := map[string]interface{}{
|
||||
"type": "hysteria2",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"password": u.User.Username(),
|
||||
"tls": getTls(security, &query),
|
||||
}
|
||||
down, _ := strconv.Atoi(query.Get("downmbps"))
|
||||
up, _ := strconv.Atoi(query.Get("upmbps"))
|
||||
obfs := query.Get("obfs")
|
||||
mport := strings.ReplaceAll(query.Get("mport"), "-", ":")
|
||||
fastopen := query.Get("fastopen")
|
||||
if down > 0 {
|
||||
hy2["down_mbps"] = down
|
||||
}
|
||||
if up > 0 {
|
||||
hy2["up_mbps"] = up
|
||||
}
|
||||
if obfs == "salamander" {
|
||||
hy2["obfs"] = map[string]interface{}{
|
||||
"type": "salamander",
|
||||
"password": query.Get("obfs-password"),
|
||||
}
|
||||
}
|
||||
if len(mport) > 0 {
|
||||
hy2["server_ports"] = strings.Split(mport, ",")
|
||||
}
|
||||
if fastopen == "1" || fastopen == "true" {
|
||||
hy2["fastopen"] = true
|
||||
}
|
||||
return &hy2, tag, nil
|
||||
}
|
||||
|
||||
func anytls(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
query, _ := url.ParseQuery(u.RawQuery)
|
||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||
port := 443
|
||||
if len(portStr) > 0 {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
}
|
||||
|
||||
security := query.Get("security")
|
||||
if len(security) == 0 {
|
||||
security = "tls"
|
||||
}
|
||||
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
anytls := map[string]interface{}{
|
||||
"type": "anytls",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"password": u.User.Username(),
|
||||
"tls": getTls(security, &query),
|
||||
}
|
||||
return &anytls, tag, nil
|
||||
}
|
||||
|
||||
func tuic(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
query, _ := url.ParseQuery(u.RawQuery)
|
||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||
port := 443
|
||||
if len(portStr) > 0 {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
}
|
||||
|
||||
security := query.Get("security")
|
||||
if len(security) == 0 {
|
||||
security = "tls"
|
||||
}
|
||||
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
password, _ := u.User.Password()
|
||||
tuic := map[string]interface{}{
|
||||
"type": "tuic",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"uuid": u.User.Username(),
|
||||
"password": password,
|
||||
"congestion_control": query.Get("congestion_control"),
|
||||
"udp_relay_mode": query.Get("udp_relay_mode"),
|
||||
"tls": getTls(security, &query),
|
||||
}
|
||||
return &tuic, tag, nil
|
||||
}
|
||||
|
||||
func ss(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
query, _ := url.ParseQuery(u.RawQuery)
|
||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||
port := 443
|
||||
if len(portStr) > 0 {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
}
|
||||
method := u.User.Username()
|
||||
password, ok := u.User.Password()
|
||||
if !ok {
|
||||
decrypted := StrOrBase64Encoded(method)
|
||||
decrypted_arr := strings.Split(decrypted, ":")
|
||||
if len(decrypted_arr) > 1 {
|
||||
method = decrypted_arr[0]
|
||||
password = strings.Join(decrypted_arr[1:], ":")
|
||||
} else {
|
||||
return nil, "", common.NewError("Unsupported shadowsocks")
|
||||
}
|
||||
}
|
||||
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
ss := map[string]interface{}{
|
||||
"type": "shadowsocks",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"method": method,
|
||||
"password": password,
|
||||
}
|
||||
|
||||
v2ray_type := query.Get("type")
|
||||
if len(v2ray_type) > 0 {
|
||||
pl_arr := []string{}
|
||||
host_header := query.Get("host")
|
||||
if query.Get("security") == "tls" {
|
||||
pl_arr = append(pl_arr, "tls")
|
||||
}
|
||||
if v2ray_type == "quic" {
|
||||
pl_arr = append(pl_arr, "mode=quic")
|
||||
}
|
||||
if len(host_header) > 0 {
|
||||
pl_arr = append(pl_arr, "host="+host_header)
|
||||
}
|
||||
ss["plugin"] = "v2ray-plugin"
|
||||
ss["plugin_opts"] = strings.Join(pl_arr, ";")
|
||||
}
|
||||
plugin := query.Get("plugin")
|
||||
if len(plugin) > 0 {
|
||||
pl_arr := strings.Split(plugin, ";")
|
||||
if len(pl_arr) > 0 {
|
||||
ss["plugin"] = pl_arr[0]
|
||||
ss["plugin_opts"] = strings.Join(pl_arr[1:], ";")
|
||||
}
|
||||
}
|
||||
return &ss, tag, nil
|
||||
}
|
||||
|
||||
func parseNaiveLink(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||
var host, portStr, username, password string
|
||||
var port int
|
||||
|
||||
switch u.Scheme {
|
||||
case "http2":
|
||||
decoded := StrOrBase64Encoded(u.Hostname())
|
||||
if idx := strings.Index(decoded, "@"); idx != -1 {
|
||||
userInfo := decoded[:idx]
|
||||
hostPort := decoded[idx+1:]
|
||||
if idx2 := strings.Index(userInfo, ":"); idx2 != -1 {
|
||||
username = userInfo[:idx2]
|
||||
password = userInfo[idx2+1:]
|
||||
} else {
|
||||
username = userInfo
|
||||
}
|
||||
host, portStr, _ = net.SplitHostPort(hostPort)
|
||||
if portStr != "" {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
} else {
|
||||
port = 443
|
||||
}
|
||||
} else {
|
||||
return nil, "", common.NewError("Invalid naive link (http2)")
|
||||
}
|
||||
case "naive+https", "naive+quic":
|
||||
host, portStr, _ = net.SplitHostPort(u.Host)
|
||||
if portStr != "" {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
} else {
|
||||
port = 443
|
||||
}
|
||||
if u.User != nil {
|
||||
username = u.User.Username()
|
||||
password, _ = u.User.Password()
|
||||
}
|
||||
default:
|
||||
return nil, "", common.NewError("Unsupported naive scheme")
|
||||
}
|
||||
|
||||
tag := u.Fragment
|
||||
if i > 0 {
|
||||
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||
}
|
||||
if tag == "" {
|
||||
tag = fmt.Sprintf("naive-%d", i)
|
||||
}
|
||||
|
||||
naive := map[string]interface{}{
|
||||
"type": "naive",
|
||||
"tag": tag,
|
||||
"server": host,
|
||||
"server_port": port,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"tls": map[string]interface{}{"enabled": true},
|
||||
}
|
||||
|
||||
query := u.Query()
|
||||
if peer := query.Get("peer"); peer != "" {
|
||||
if tls, ok := naive["tls"].(map[string]interface{}); ok {
|
||||
tls["server_name"] = peer
|
||||
}
|
||||
}
|
||||
if insecure := query.Get("insecure"); insecure == "1" || insecure == "true" {
|
||||
if tls, ok := naive["tls"].(map[string]interface{}); ok {
|
||||
tls["insecure"] = true
|
||||
}
|
||||
}
|
||||
if alpn := query.Get("alpn"); alpn != "" {
|
||||
if tls, ok := naive["tls"].(map[string]interface{}); ok {
|
||||
tls["alpn"] = strings.Split(alpn, ",")
|
||||
}
|
||||
}
|
||||
if u.Scheme == "naive+quic" {
|
||||
naive["quic"] = true
|
||||
}
|
||||
|
||||
return &naive, tag, nil
|
||||
}
|
||||
|
||||
func getTransport(tp_type string, q *url.Values) map[string]interface{} {
|
||||
transport := map[string]interface{}{}
|
||||
tp_host := q.Get("host")
|
||||
tp_path := q.Get("path")
|
||||
switch strings.ToLower(tp_type) {
|
||||
case "tcp", "":
|
||||
if q.Get("headerType") == "http" {
|
||||
transport["type"] = "http"
|
||||
if len(tp_host) > 0 {
|
||||
transport["host"] = strings.Split(tp_host, ",")
|
||||
}
|
||||
transport["path"] = tp_path
|
||||
}
|
||||
case "http", "h2":
|
||||
transport["type"] = "http"
|
||||
if len(tp_host) > 0 {
|
||||
transport["host"] = strings.Split(tp_host, ",")
|
||||
}
|
||||
transport["path"] = tp_path
|
||||
case "ws":
|
||||
transport["type"] = "ws"
|
||||
transport["path"] = tp_path
|
||||
if len(tp_host) > 0 {
|
||||
transport["headers"] = map[string]interface{}{
|
||||
"Host": tp_host,
|
||||
}
|
||||
}
|
||||
case "quic":
|
||||
transport["type"] = "quic"
|
||||
case "grpc":
|
||||
transport["type"] = "grpc"
|
||||
transport["service_name"] = q.Get("serviceName")
|
||||
case "httpupgrade":
|
||||
transport["type"] = "httpupgrade"
|
||||
transport["path"] = tp_path
|
||||
transport["host"] = tp_host
|
||||
}
|
||||
return transport
|
||||
}
|
||||
|
||||
func getTls(security string, q *url.Values) map[string]interface{} {
|
||||
tls := map[string]interface{}{}
|
||||
tls_fp := q.Get("fp")
|
||||
tls_sni := q.Get("sni")
|
||||
tls_allow_insecure := q.Get("allowInsecure")
|
||||
tls_insecure := q.Get("insecure")
|
||||
tls_alpn := q.Get("alpn")
|
||||
tls_ech := q.Get("ech")
|
||||
disable_sni := q.Get("disable_sni")
|
||||
switch security {
|
||||
case "tls":
|
||||
tls["enabled"] = true
|
||||
case "reality":
|
||||
tls["enabled"] = true
|
||||
tls["reality"] = map[string]interface{}{
|
||||
"enabled": true,
|
||||
"public_key": q.Get("pbk"),
|
||||
"short_id": q.Get("sid"),
|
||||
}
|
||||
}
|
||||
if len(tls_sni) > 0 {
|
||||
tls["server_name"] = tls_sni
|
||||
}
|
||||
if len(tls_alpn) > 0 {
|
||||
tls["alpn"] = strings.Split(tls_alpn, ",")
|
||||
}
|
||||
if tls_insecure == "1" || tls_insecure == "true" || tls_allow_insecure == "1" || tls_allow_insecure == "true" {
|
||||
tls["insecure"] = true
|
||||
}
|
||||
if len(tls_fp) > 0 {
|
||||
tls["utls"] = map[string]interface{}{
|
||||
"enabled": true,
|
||||
"fingerprint": tls_fp,
|
||||
}
|
||||
}
|
||||
if len(tls_ech) > 0 {
|
||||
tls["ech"] = map[string]interface{}{
|
||||
"enabled": true,
|
||||
"config": []string{
|
||||
tls_ech,
|
||||
},
|
||||
}
|
||||
}
|
||||
if disable_sni == "1" || disable_sni == "true" {
|
||||
tls["disable_sni"] = true
|
||||
}
|
||||
return tls
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/alireza0/s-ui/util/common"
|
||||
|
||||
"github.com/alireza0/s-ui/database/model"
|
||||
)
|
||||
|
||||
// Fill Inbound's out_json
|
||||
func FillOutJson(i *model.Inbound, hostname string) error {
|
||||
switch i.Type {
|
||||
case "direct", "tun", "redirect", "tproxy":
|
||||
return nil
|
||||
}
|
||||
var outJson map[string]interface{}
|
||||
err := json.Unmarshal(i.OutJson, &outJson)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if outJson == nil {
|
||||
outJson = make(map[string]interface{})
|
||||
}
|
||||
|
||||
if i.TlsId > 0 {
|
||||
addTls(&outJson, i.Tls)
|
||||
} else {
|
||||
delete(outJson, "tls")
|
||||
}
|
||||
|
||||
inbound, err := i.MarshalFull()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outJson["type"] = i.Type
|
||||
outJson["tag"] = i.Tag
|
||||
outJson["server"] = hostname
|
||||
outJson["server_port"] = (*inbound)["listen_port"]
|
||||
|
||||
switch i.Type {
|
||||
case "http", "socks", "mixed", "anytls":
|
||||
case "naive":
|
||||
naiveOut(&outJson, *inbound)
|
||||
case "shadowsocks":
|
||||
shadowsocksOut(&outJson, *inbound)
|
||||
case "shadowtls":
|
||||
shadowTlsOut(&outJson, *inbound)
|
||||
case "hysteria":
|
||||
hysteriaOut(&outJson, *inbound)
|
||||
case "hysteria2":
|
||||
hysteria2Out(&outJson, *inbound)
|
||||
case "tuic":
|
||||
tuicOut(&outJson, *inbound)
|
||||
case "vless":
|
||||
vlessOut(&outJson, *inbound)
|
||||
case "trojan":
|
||||
trojanOut(&outJson, *inbound)
|
||||
case "vmess":
|
||||
vmessOut(&outJson, *inbound)
|
||||
default:
|
||||
for key := range outJson {
|
||||
delete(outJson, key)
|
||||
}
|
||||
}
|
||||
|
||||
i.OutJson, err = json.MarshalIndent(outJson, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addTls function
|
||||
func addTls(out *map[string]interface{}, tls *model.Tls) {
|
||||
var tlsServer, tlsConfig map[string]interface{}
|
||||
err := json.Unmarshal(tls.Server, &tlsServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(tls.Client, &tlsConfig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if enabled, ok := tlsServer["enabled"]; ok {
|
||||
tlsConfig["enabled"] = enabled
|
||||
}
|
||||
if serverName, ok := tlsServer["server_name"]; ok {
|
||||
tlsConfig["server_name"] = serverName
|
||||
}
|
||||
if alpn, ok := tlsServer["alpn"]; ok {
|
||||
tlsConfig["alpn"] = alpn
|
||||
}
|
||||
if minVersion, ok := tlsServer["min_version"]; ok {
|
||||
tlsConfig["min_version"] = minVersion
|
||||
}
|
||||
if maxVersion, ok := tlsServer["max_version"]; ok {
|
||||
tlsConfig["max_version"] = maxVersion
|
||||
}
|
||||
if certificate, ok := tlsServer["certificate"]; ok {
|
||||
tlsConfig["certificate"] = certificate
|
||||
}
|
||||
if cipherSuites, ok := tlsServer["cipher_suites"]; ok {
|
||||
tlsConfig["cipher_suites"] = cipherSuites
|
||||
}
|
||||
if reality, ok := tlsServer["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) {
|
||||
realityConfig := tlsConfig["reality"].(map[string]interface{})
|
||||
realityConfig["enabled"] = true
|
||||
if shortIDs, ok := reality["short_id"].([]interface{}); ok && len(shortIDs) > 0 {
|
||||
realityConfig["short_id"] = shortIDs[common.RandomInt(len(shortIDs))]
|
||||
}
|
||||
tlsConfig["reality"] = realityConfig
|
||||
}
|
||||
if ech, ok := tlsServer["ech"].(map[string]interface{}); ok && ech["enabled"].(bool) {
|
||||
echConfig := tlsConfig["ech"].(map[string]interface{})
|
||||
echConfig["enabled"] = true
|
||||
echConfig["pq_signature_schemes_enabled"] = ech["pq_signature_schemes_enabled"]
|
||||
echConfig["dynamic_record_sizing_disabled"] = ech["dynamic_record_sizing_disabled"]
|
||||
tlsConfig["ech"] = echConfig
|
||||
}
|
||||
|
||||
(*out)["tls"] = tlsConfig
|
||||
}
|
||||
|
||||
func naiveOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
if quic_congestion_control, ok := inbound["quic_congestion_control"].(string); ok {
|
||||
(*out)["quic"] = true
|
||||
switch quic_congestion_control {
|
||||
case "bbr_standard":
|
||||
(*out)["quic_congestion_control"] = "bbr"
|
||||
case "bbr2_variant":
|
||||
(*out)["quic_congestion_control"] = "bbr2"
|
||||
default:
|
||||
(*out)["quic_congestion_control"] = quic_congestion_control
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func shadowsocksOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
if method, ok := inbound["method"].(string); ok {
|
||||
(*out)["method"] = method
|
||||
}
|
||||
}
|
||||
|
||||
func shadowTlsOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
if version, ok := inbound["version"].(float64); ok && int(version) == 3 {
|
||||
(*out)["version"] = 3
|
||||
} else {
|
||||
for key := range *out {
|
||||
delete(*out, key)
|
||||
}
|
||||
}
|
||||
(*out)["tls"] = map[string]interface{}{"enabled": true}
|
||||
}
|
||||
|
||||
func hysteriaOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
delete(*out, "down_mbps")
|
||||
delete(*out, "up_mbps")
|
||||
delete(*out, "obfs")
|
||||
delete(*out, "recv_window_conn")
|
||||
delete(*out, "disable_mtu_discovery")
|
||||
|
||||
if upMbps, ok := inbound["down_mbps"]; ok {
|
||||
(*out)["up_mbps"] = upMbps
|
||||
}
|
||||
if downMbps, ok := inbound["up_mbps"]; ok {
|
||||
(*out)["down_mbps"] = downMbps
|
||||
}
|
||||
if obfs, ok := inbound["obfs"]; ok {
|
||||
(*out)["obfs"] = obfs
|
||||
}
|
||||
if recvWindow, ok := inbound["recv_window_conn"]; ok {
|
||||
(*out)["recv_window_conn"] = recvWindow
|
||||
}
|
||||
if disableMTU, ok := inbound["disable_mtu_discovery"]; ok {
|
||||
(*out)["disable_mtu_discovery"] = disableMTU
|
||||
}
|
||||
}
|
||||
|
||||
func hysteria2Out(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
delete(*out, "down_mbps")
|
||||
delete(*out, "up_mbps")
|
||||
delete(*out, "obfs")
|
||||
|
||||
if upMbps, ok := inbound["down_mbps"]; ok {
|
||||
(*out)["up_mbps"] = upMbps
|
||||
}
|
||||
if downMbps, ok := inbound["up_mbps"]; ok {
|
||||
(*out)["down_mbps"] = downMbps
|
||||
}
|
||||
if obfs, ok := inbound["obfs"]; ok {
|
||||
(*out)["obfs"] = obfs
|
||||
}
|
||||
}
|
||||
|
||||
func tuicOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
delete(*out, "zero_rtt_handshake")
|
||||
delete(*out, "heartbeat")
|
||||
if congestionControl, ok := inbound["congestion_control"].(string); ok {
|
||||
(*out)["congestion_control"] = congestionControl
|
||||
} else {
|
||||
(*out)["congestion_control"] = "cubic"
|
||||
}
|
||||
if zeroRTT, ok := inbound["zero_rtt_handshake"].(bool); ok {
|
||||
(*out)["zero_rtt_handshake"] = zeroRTT
|
||||
}
|
||||
if heartbeat, ok := inbound["heartbeat"]; ok {
|
||||
(*out)["heartbeat"] = heartbeat
|
||||
}
|
||||
}
|
||||
|
||||
func vlessOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
delete(*out, "transport")
|
||||
if transport, ok := inbound["transport"]; ok {
|
||||
(*out)["transport"] = transport
|
||||
}
|
||||
}
|
||||
|
||||
func trojanOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
delete(*out, "transport")
|
||||
if transport, ok := inbound["transport"]; ok {
|
||||
(*out)["transport"] = transport
|
||||
}
|
||||
}
|
||||
|
||||
func vmessOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||
(*out)["alter_id"] = 0
|
||||
delete(*out, "transport")
|
||||
if transport, ok := inbound["transport"]; ok {
|
||||
(*out)["transport"] = transport
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alireza0/s-ui/database/model"
|
||||
)
|
||||
|
||||
func GetHeaders(client *model.Client, updateInterval int) []string {
|
||||
var headers []string
|
||||
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", client.Up, client.Down, client.Volume, client.Expiry))
|
||||
headers = append(headers, fmt.Sprintf("%d", updateInterval))
|
||||
headers = append(headers, client.Name)
|
||||
return headers
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/alireza0/s-ui/logger"
|
||||
"github.com/alireza0/s-ui/util/common"
|
||||
)
|
||||
|
||||
func GetExternalLink(url string) string {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
logger.Warning("sub: Error making HTTP request:", err)
|
||||
return ""
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
logger.Warning("sub: Error reading response body:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
data := StrOrBase64Encoded(string(body))
|
||||
return data
|
||||
}
|
||||
|
||||
func GetExternalSub(url string) ([]map[string]interface{}, error) {
|
||||
var err error
|
||||
var result []map[string]interface{}
|
||||
|
||||
if len(url) == 0 {
|
||||
return nil, common.NewError("no url")
|
||||
}
|
||||
|
||||
data := GetExternalLink(url)
|
||||
if len(data) == 0 {
|
||||
return nil, common.NewError("no result")
|
||||
}
|
||||
|
||||
// if the data is a JSON object
|
||||
if strings.HasPrefix(data, "{") && strings.HasSuffix(data, "}") {
|
||||
var jsonData map[string]interface{}
|
||||
err = json.Unmarshal([]byte(data), &jsonData)
|
||||
if err != nil {
|
||||
logger.Warning("sub: Error unmarshalling JSON:", err)
|
||||
return nil, err
|
||||
}
|
||||
outbounds, ok := jsonData["outbounds"].([]any)
|
||||
if !ok {
|
||||
logger.Warning("sub: Error getting outbounds:", err)
|
||||
return nil, err
|
||||
}
|
||||
for _, outbound := range outbounds {
|
||||
outboundMap, ok := outbound.(map[string]interface{})
|
||||
if ok && len(outboundMap) > 0 {
|
||||
oType, _ := outboundMap["type"].(string)
|
||||
switch oType {
|
||||
case "urltest":
|
||||
case "direct":
|
||||
case "selector":
|
||||
case "block":
|
||||
continue
|
||||
default:
|
||||
result = append(result, outboundMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil, common.NewError("no result")
|
||||
}
|
||||
return result, nil
|
||||
} else {
|
||||
// if data is a text
|
||||
links := strings.Split(data, "\n")
|
||||
for _, link := range links {
|
||||
linkToJson, _, err := GetOutbound(link, 0)
|
||||
if err == nil {
|
||||
result = append(result, *linkToJson)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil, common.NewError("no result")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user