diff --git a/go.sum b/go.sum index 0e86b547..8484df35 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,6 @@ github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.7 h1:d3sry5vGgVq/OpgozRUNP6xBsSo0mtNdwliApw+SAMQ= -github.com/bytedance/sonic v1.8.7/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= diff --git a/logger/logger.go b/logger/logger.go index cb5e8360..405f2ec7 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,8 +1,9 @@ package logger import ( - "github.com/op/go-logging" "os" + + "github.com/op/go-logging" ) var logger *logging.Logger diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index 3370aa07..195155cf 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -440,18 +440,26 @@ class QuicStreamSettings extends XrayCommonClass { } class GrpcStreamSettings extends XrayCommonClass { - constructor(serviceName="") { + constructor( + serviceName="", + multiMode=false + ) { super(); this.serviceName = serviceName; + this.multiMode = multiMode; } static fromJson(json={}) { - return new GrpcStreamSettings(json.serviceName); + return new GrpcStreamSettings( + json.serviceName, + json.multiMode + ); } toJson() { return { serviceName: this.serviceName, + multiMode: this.multiMode } } } @@ -1246,50 +1254,6 @@ class Inbound extends XrayCommonClass { if (this.protocol !== Protocols.VMESS) { return ''; } - let network = this.stream.network; - let type = 'none'; - let host = ''; - let path = ''; - if (network === 'tcp') { - let tcp = this.stream.tcp; - type = tcp.type; - if (type === 'http') { - let request = tcp.request; - path = request.path.join(','); - let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - host = request.headers[index].value; - } - } - } else if (network === 'kcp') { - let kcp = this.stream.kcp; - type = kcp.type; - path = kcp.seed; - } else if (network === 'ws') { - let ws = this.stream.ws; - path = ws.path; - let index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - host = ws.headers[index].value; - } - } else if (network === 'http') { - network = 'h2'; - path = this.stream.http.path; - host = this.stream.http.host.join(','); - } else if (network === 'quic') { - type = this.stream.quic.type; - host = this.stream.quic.security; - path = this.stream.quic.key; - } else if (network === 'grpc') { - path = this.stream.grpc.serviceName; - } - - if (this.stream.security === 'tls') { - if (!ObjectUtil.isEmpty(this.stream.tls.server)) { - address = this.stream.tls.server; - } - } - let obj = { v: '2', ps: remark, @@ -1297,16 +1261,66 @@ class Inbound extends XrayCommonClass { port: this.port, id: this.settings.vmesses[clientIndex].id, aid: this.settings.vmesses[clientIndex].alterId, - net: network, - type: type, - host: host, - path: path, + net: this.stream.network, + type: 'none', tls: this.stream.security, - sni: this.stream.tls.settings.serverName, - fp: this.stream.tls.settings.fingerprint, - alpn: this.stream.tls.alpn.join(','), - allowInsecure: this.stream.tls.settings.allowInsecure, }; + let network = this.stream.network; + if (network === 'tcp') { + let tcp = this.stream.tcp; + obj.type = tcp.type; + if (tcp.type === 'http') { + let request = tcp.request; + obj.path = request.path.join(','); + let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + obj.host = request.headers[index].value; + } + } + } else if (network === 'kcp') { + let kcp = this.stream.kcp; + obj.type = kcp.type; + obj.path = kcp.seed; + } else if (network === 'ws') { + let ws = this.stream.ws; + obj.path = ws.path; + let index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + obj.host = ws.headers[index].value; + } + } else if (network === 'http') { + obj.net = 'h2'; + obj.path = this.stream.http.path; + obj.host = this.stream.http.host.join(','); + } else if (network === 'quic') { + obj.type = this.stream.quic.type; + obj.host = this.stream.quic.security; + obj.path = this.stream.quic.key; + } else if (network === 'grpc') { + obj.path = this.stream.grpc.serviceName; + if (this.stream.grpc.multiMode){ + obj.type = 'multi' + } + } + + if (this.stream.security === 'tls') { + if (!ObjectUtil.isEmpty(this.stream.tls.server)) { + obj.add = this.stream.tls.server; + } + if (!ObjectUtil.isEmpty(this.stream.tls.settings.serverName)){ + obj.sni = this.stream.tls.settings.serverName; + } + if (!ObjectUtil.isEmpty(this.stream.tls.settings.fingerprint)){ + obj.fp = this.stream.tls.settings.fingerprint; + } + if (this.stream.tls.alpn.length>0){ + obj.alpn = this.stream.tls.alpn.join(','); + } + if (this.stream.tls.settings.allowInsecure){ + obj.allowInsecure = this.stream.tls.settings.allowInsecure; + } + } + return 'vmess://' + base64(JSON.stringify(obj, null, 2)); } @@ -1359,6 +1373,9 @@ class Inbound extends XrayCommonClass { case "grpc": const grpc = this.stream.grpc; params.set("serviceName", grpc.serviceName); + if(grpc.multiMode){ + params.set("mode", "multi"); + } break; } @@ -1476,6 +1493,9 @@ class Inbound extends XrayCommonClass { case "grpc": const grpc = this.stream.grpc; params.set("serviceName", grpc.serviceName); + if(grpc.multiMode){ + params.set("mode", "multi"); + } break; } diff --git a/web/global/global.go b/web/global/global.go index 09d0683a..7ded94e7 100644 --- a/web/global/global.go +++ b/web/global/global.go @@ -2,8 +2,9 @@ package global import ( "context" - "github.com/robfig/cron/v3" _ "unsafe" + + "github.com/robfig/cron/v3" ) var webServer WebServer diff --git a/web/html/xui/form/stream/stream_grpc.html b/web/html/xui/form/stream/stream_grpc.html index 4e57d225..21c95f99 100644 --- a/web/html/xui/form/stream/stream_grpc.html +++ b/web/html/xui/form/stream/stream_grpc.html @@ -3,5 +3,8 @@ + + + {{end}} \ No newline at end of file diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html index 4e8c7dae..489a9a61 100644 --- a/web/html/xui/inbound_info_modal.html +++ b/web/html/xui/inbound_info_modal.html @@ -41,6 +41,7 @@ diff --git a/web/service/sub.go b/web/service/sub.go index ba70fe43..2aaee06a 100644 --- a/web/service/sub.go +++ b/web/service/sub.go @@ -102,80 +102,89 @@ func (s *SubService) getLink(inbound *model.Inbound, email string) string { } func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { - address := s.address if inbound.Protocol != model.VMess { return "" } + obj := map[string]interface{}{ + "v": "2", + "ps": email, + "add": s.address, + "port": inbound.Port, + "type": "none", + } var stream map[string]interface{} json.Unmarshal([]byte(inbound.StreamSettings), &stream) network, _ := stream["network"].(string) - typeStr := "none" - host := "" - path := "" - sni := "" - fp := "" - var alpn []string - allowInsecure := false + obj["net"] = network switch network { case "tcp": tcp, _ := stream["tcpSettings"].(map[string]interface{}) header, _ := tcp["header"].(map[string]interface{}) - typeStr, _ = header["type"].(string) + typeStr, _ := header["type"].(string) + obj["type"] = typeStr if typeStr == "http" { request := header["request"].(map[string]interface{}) requestPath, _ := request["path"].([]interface{}) - path = requestPath[0].(string) + obj["path"] = requestPath[0].(string) headers, _ := request["headers"].(map[string]interface{}) - host = searchHost(headers) + obj["host"] = searchHost(headers) } case "kcp": kcp, _ := stream["kcpSettings"].(map[string]interface{}) header, _ := kcp["header"].(map[string]interface{}) - typeStr, _ = header["type"].(string) - path, _ = kcp["seed"].(string) + obj["type"], _ = header["type"].(string) + obj["path"], _ = kcp["seed"].(string) case "ws": ws, _ := stream["wsSettings"].(map[string]interface{}) - path = ws["path"].(string) + obj["path"] = ws["path"].(string) headers, _ := ws["headers"].(map[string]interface{}) - host = searchHost(headers) + obj["host"] = searchHost(headers) case "http": - network = "h2" + obj["net"] = "h2" http, _ := stream["httpSettings"].(map[string]interface{}) - path, _ = http["path"].(string) - host = searchHost(http) + obj["path"], _ = http["path"].(string) + obj["host"] = searchHost(http) case "quic": quic, _ := stream["quicSettings"].(map[string]interface{}) header := quic["header"].(map[string]interface{}) - typeStr, _ = header["type"].(string) - host, _ = quic["security"].(string) - path, _ = quic["key"].(string) + obj["type"], _ = header["type"].(string) + obj["host"], _ = quic["security"].(string) + obj["path"], _ = quic["key"].(string) case "grpc": grpc, _ := stream["grpcSettings"].(map[string]interface{}) - path = grpc["serviceName"].(string) + obj["path"] = grpc["serviceName"].(string) + if grpc["multiMode"].(bool) { + obj["type"] = "multi" + } } security, _ := stream["security"].(string) + obj["tls"] = security if security == "tls" { tlsSetting, _ := stream["tlsSettings"].(map[string]interface{}) alpns, _ := tlsSetting["alpn"].([]interface{}) - for _, a := range alpns { - alpn = append(alpn, a.(string)) + if len(alpns) > 0 { + var alpn []string + for _, a := range alpns { + alpn = append(alpn, a.(string)) + } + obj["alpn"] = strings.Join(alpn, ",") } tlsSettings, _ := searchKey(tlsSetting, "settings") if tlsSetting != nil { if sniValue, ok := searchKey(tlsSettings, "serverName"); ok { - sni, _ = sniValue.(string) + obj["sni"], _ = sniValue.(string) } if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok { - fp, _ = fpValue.(string) + obj["fp"], _ = fpValue.(string) } if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok { - allowInsecure, _ = insecure.(bool) + obj["allowInsecure"], _ = insecure.(bool) } } serverName, _ := tlsSetting["serverName"].(string) if serverName != "" { - address = serverName + obj["add"] = serverName } } @@ -187,24 +196,9 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { break } } + obj["id"] = clients[clientIndex].ID + obj["aid"] = clients[clientIndex].AlterIds - obj := map[string]interface{}{ - "v": "2", - "ps": email, - "add": address, - "port": inbound.Port, - "id": clients[clientIndex].ID, - "aid": clients[clientIndex].AlterIds, - "net": network, - "type": typeStr, - "host": host, - "path": path, - "tls": security, - "sni": sni, - "fp": fp, - "alpn": strings.Join(alpn, ","), - "allowInsecure": allowInsecure, - } jsonStr, _ := json.MarshalIndent(obj, "", " ") return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr) } @@ -266,6 +260,9 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { case "grpc": grpc, _ := stream["grpcSettings"].(map[string]interface{}) params["serviceName"] = grpc["serviceName"].(string) + if grpc["multiMode"].(bool) { + params["mode"] = "multi" + } } security, _ := stream["security"].(string) @@ -444,6 +441,9 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string case "grpc": grpc, _ := stream["grpcSettings"].(map[string]interface{}) params["serviceName"] = grpc["serviceName"].(string) + if grpc["multiMode"].(bool) { + params["mode"] = "multi" + } } security, _ := stream["security"].(string)