mirror of
https://github.com/bestnite/sub2clash.git
synced 2025-07-04 11:52:34 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
0fa95888cb | |||
b44703fa0f | |||
b256c5e809 | |||
307c36aa8d | |||
06da2e91c2 | |||
33d37e631b |
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
P "github.com/bestnite/sub2clash/model/proxy"
|
P "github.com/bestnite/sub2clash/model/proxy"
|
||||||
)
|
)
|
||||||
@ -32,8 +33,13 @@ func ParsePort(portStr string) (int, error) {
|
|||||||
return port, nil
|
return port, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isLikelyBase64 不严格判断是否是合法的 Base64, 很多分享链接不符合 Base64 规范
|
||||||
func isLikelyBase64(s string) bool {
|
func isLikelyBase64(s string) bool {
|
||||||
if len(s)%4 == 0 && strings.HasSuffix(s, "=") && !strings.Contains(strings.TrimSuffix(s, "="), "=") {
|
if strings.TrimSpace(s) == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(strings.TrimSuffix(s, "="), "=") {
|
||||||
s = strings.TrimSuffix(s, "=")
|
s = strings.TrimSuffix(s, "=")
|
||||||
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
for _, c := range s {
|
for _, c := range s {
|
||||||
@ -41,17 +47,25 @@ func isLikelyBase64(s string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decoded, err := DecodeBase64(s)
|
||||||
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
if !utf8.ValidString(decoded) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeBase64(s string) (string, error) {
|
func DecodeBase64(s string) (string, error) {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
|
|
||||||
if strings.Contains(s, "-") || strings.Contains(s, "_") {
|
if strings.Contains(s, "-") || strings.Contains(s, "_") {
|
||||||
s = strings.Replace(s, "-", "+", -1)
|
s = strings.ReplaceAll(s, "-", "+")
|
||||||
s = strings.Replace(s, "_", "/", -1)
|
s = strings.ReplaceAll(s, "_", "/")
|
||||||
}
|
}
|
||||||
if len(s)%4 != 0 {
|
if len(s)%4 != 0 {
|
||||||
s += strings.Repeat("=", 4-len(s)%4)
|
s += strings.Repeat("=", 4-len(s)%4)
|
||||||
|
@ -58,7 +58,17 @@ func (p *TrojanParser) Parse(proxy string) (P.Proxy, error) {
|
|||||||
remarks = strings.TrimSpace(remarks)
|
remarks = strings.TrimSpace(remarks)
|
||||||
|
|
||||||
query := link.Query()
|
query := link.Query()
|
||||||
network, security, alpnStr, sni, pbk, sid, fp, path, host, serviceName, udp := query.Get("type"), query.Get("security"), query.Get("alpn"), query.Get("sni"), query.Get("pbk"), query.Get("sid"), query.Get("fp"), query.Get("path"), query.Get("host"), query.Get("serviceName"), query.Get("udp")
|
network, security, alpnStr, sni, pbk, sid, fp, path, host, serviceName, udp, insecure := query.Get("type"), query.Get("security"), query.Get("alpn"), query.Get("sni"), query.Get("pbk"), query.Get("sid"), query.Get("fp"), query.Get("path"), query.Get("host"), query.Get("serviceName"), query.Get("udp"), query.Get("allowInsecure")
|
||||||
|
|
||||||
|
insecureBool := insecure == "1"
|
||||||
|
result := P.Trojan{
|
||||||
|
Server: server,
|
||||||
|
Port: port,
|
||||||
|
Password: password,
|
||||||
|
Network: network,
|
||||||
|
UDP: udp == "true",
|
||||||
|
SkipCertVerify: insecureBool,
|
||||||
|
}
|
||||||
|
|
||||||
var alpn []string
|
var alpn []string
|
||||||
if strings.Contains(alpnStr, ",") {
|
if strings.Contains(alpnStr, ",") {
|
||||||
@ -66,27 +76,23 @@ func (p *TrojanParser) Parse(proxy string) (P.Proxy, error) {
|
|||||||
} else {
|
} else {
|
||||||
alpn = nil
|
alpn = nil
|
||||||
}
|
}
|
||||||
|
if len(alpn) > 0 {
|
||||||
result := P.Trojan{
|
result.ALPN = alpn
|
||||||
Server: server,
|
|
||||||
Port: port,
|
|
||||||
Password: password,
|
|
||||||
Network: network,
|
|
||||||
UDP: udp == "true",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == "xtls" || security == "tls" {
|
if fp != "" {
|
||||||
result.ALPN = alpn
|
result.ClientFingerprint = fp
|
||||||
|
}
|
||||||
|
|
||||||
|
if sni != "" {
|
||||||
result.SNI = sni
|
result.SNI = sni
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == "reality" {
|
if security == "reality" {
|
||||||
result.SNI = sni
|
|
||||||
result.RealityOpts = P.RealityOptions{
|
result.RealityOpts = P.RealityOptions{
|
||||||
PublicKey: pbk,
|
PublicKey: pbk,
|
||||||
ShortID: sid,
|
ShortID: sid,
|
||||||
}
|
}
|
||||||
result.Fingerprint = fp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if network == "ws" {
|
if network == "ws" {
|
||||||
|
@ -57,6 +57,7 @@ func (p *VlessParser) Parse(proxy string) (P.Proxy, error) {
|
|||||||
} else {
|
} else {
|
||||||
alpn = nil
|
alpn = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
remarks := link.Fragment
|
remarks := link.Fragment
|
||||||
if remarks == "" {
|
if remarks == "" {
|
||||||
remarks = fmt.Sprintf("%s:%s", server, portStr)
|
remarks = fmt.Sprintf("%s:%s", server, portStr)
|
||||||
@ -69,24 +70,31 @@ func (p *VlessParser) Parse(proxy string) (P.Proxy, error) {
|
|||||||
UUID: uuid,
|
UUID: uuid,
|
||||||
Flow: flow,
|
Flow: flow,
|
||||||
UDP: udp == "true",
|
UDP: udp == "true",
|
||||||
|
SkipCertVerify: insecureBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(alpn) > 0 {
|
||||||
|
result.ALPN = alpn
|
||||||
|
}
|
||||||
|
|
||||||
|
if fp != "" {
|
||||||
|
result.ClientFingerprint = fp
|
||||||
|
}
|
||||||
|
|
||||||
|
if sni != "" {
|
||||||
|
result.ServerName = sni
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
result.TLS = true
|
result.TLS = true
|
||||||
result.ALPN = alpn
|
|
||||||
result.SkipCertVerify = insecureBool
|
|
||||||
result.Fingerprint = fp
|
|
||||||
result.ServerName = sni
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == "reality" {
|
if security == "reality" {
|
||||||
result.TLS = true
|
result.TLS = true
|
||||||
result.ServerName = sni
|
|
||||||
result.RealityOpts = P.RealityOptions{
|
result.RealityOpts = P.RealityOptions{
|
||||||
PublicKey: pbk,
|
PublicKey: pbk,
|
||||||
ShortID: sid,
|
ShortID: sid,
|
||||||
}
|
}
|
||||||
result.Fingerprint = fp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _type == "ws" {
|
if _type == "ws" {
|
||||||
|
@ -99,6 +99,13 @@ func (p *VmessParser) Parse(proxy string) (P.Proxy, error) {
|
|||||||
name = vmess.Ps
|
name = vmess.Ps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var alpn []string
|
||||||
|
if strings.Contains(vmess.Alpn, ",") {
|
||||||
|
alpn = strings.Split(vmess.Alpn, ",")
|
||||||
|
} else {
|
||||||
|
alpn = nil
|
||||||
|
}
|
||||||
|
|
||||||
result := P.Vmess{
|
result := P.Vmess{
|
||||||
Server: vmess.Add,
|
Server: vmess.Add,
|
||||||
Port: port,
|
Port: port,
|
||||||
@ -107,19 +114,22 @@ func (p *VmessParser) Parse(proxy string) (P.Proxy, error) {
|
|||||||
Cipher: vmess.Scy,
|
Cipher: vmess.Scy,
|
||||||
}
|
}
|
||||||
|
|
||||||
if vmess.Tls == "tls" {
|
if len(alpn) > 0 {
|
||||||
var alpn []string
|
|
||||||
if strings.Contains(vmess.Alpn, ",") {
|
|
||||||
alpn = strings.Split(vmess.Alpn, ",")
|
|
||||||
} else {
|
|
||||||
alpn = nil
|
|
||||||
}
|
|
||||||
result.TLS = true
|
|
||||||
result.Fingerprint = vmess.Fp
|
|
||||||
result.ALPN = alpn
|
result.ALPN = alpn
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmess.Fp != "" {
|
||||||
|
result.ClientFingerprint = vmess.Fp
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmess.Sni != "" {
|
||||||
result.ServerName = vmess.Sni
|
result.ServerName = vmess.Sni
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if vmess.Tls == "tls" {
|
||||||
|
result.TLS = true
|
||||||
|
}
|
||||||
|
|
||||||
if vmess.Net == "ws" {
|
if vmess.Net == "ws" {
|
||||||
if vmess.Path == "" {
|
if vmess.Path == "" {
|
||||||
vmess.Path = "/"
|
vmess.Path = "/"
|
||||||
|
@ -168,7 +168,7 @@ func GetRawConfHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := http.Get(strings.TrimSuffix(config.GlobalConfig.Address, "/") + "/" + shortLink.Url)
|
response, err := http.Get("http://" + strings.TrimSuffix(config.GlobalConfig.Address, "/") + "/" + shortLink.Url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondWithError(c, http.StatusInternalServerError, "请求错误: "+err.Error())
|
respondWithError(c, http.StatusInternalServerError, "请求错误: "+err.Error())
|
||||||
return
|
return
|
||||||
|
@ -27,11 +27,10 @@ func SubHandler(model M.ClashType, template string) func(c *gin.Context) {
|
|||||||
|
|
||||||
if len(query.Subs) == 1 {
|
if len(query.Subs) == 1 {
|
||||||
userInfoHeader, err := common.FetchSubscriptionUserInfo(query.Subs[0], "clash", config.GlobalConfig.RequestRetryTimes)
|
userInfoHeader, err := common.FetchSubscriptionUserInfo(query.Subs[0], "clash", config.GlobalConfig.RequestRetryTimes)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
c.String(http.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
c.Header("subscription-userinfo", userInfoHeader)
|
c.Header("subscription-userinfo", userInfoHeader)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if query.NodeListMode {
|
if query.NodeListMode {
|
||||||
nodelist := M.NodeList{}
|
nodelist := M.NodeList{}
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
<label for="endpoint">客户端类型:</label>
|
<label for="endpoint">客户端类型:</label>
|
||||||
<select class="form-control" id="endpoint" name="endpoint">
|
<select class="form-control" id="endpoint" name="endpoint">
|
||||||
<option value="clash">Clash</option>
|
<option value="clash">Clash</option>
|
||||||
<option value="meta">Clash.Meta</option>
|
<option value="meta" selected>Clash.Meta</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!-- Template -->
|
<!-- Template -->
|
||||||
@ -75,9 +75,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- User Agent -->
|
<!-- User Agent -->
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<label for="user-agent">ua标识:</label>
|
<label for="user-agent">UA 标识:</label>
|
||||||
<textarea class="form-control" id="user-agent" name="user-agent"
|
<textarea class="form-control" id="user-agent" name="user-agent"
|
||||||
placeholder="用于获取订阅的http请求中的user-agent标识(可选)" rows="3"></textarea>
|
placeholder="用于获取订阅的 http 请求中的 User-Agent 标识(可选)" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<!-- Refresh -->
|
<!-- Refresh -->
|
||||||
<div class="form-check mb-3">
|
<div class="form-check mb-3">
|
||||||
|
Reference in New Issue
Block a user