Files
proxypool/pkg/proxy/trojan.go
2020-09-04 16:40:50 +08:00

166 lines
3.4 KiB
Go

package proxy
import (
"encoding/json"
"errors"
"math/rand"
"net"
"net/url"
"regexp"
"strconv"
"strings"
)
var (
ErrorNotTrojanink = errors.New("not a correct trojan link")
)
type Trojan struct {
Base
Password string `yaml:"password" json:"password"`
ALPN []string `yaml:"alpn,omitempty" json:"alpn,omitempty"`
SNI string `yaml:"sni,omitempty" json:"sni,omitempty"`
SkipCertVerify bool `yaml:"skip-cert-verify,omitempty" json:"skip-cert-verify,omitempty"`
UDP bool `yaml:"udp,omitempty" json:"udp,omitempty"`
}
/**
- name: "trojan"
type: trojan
server: server
port: 443
password: yourpsk
# udp: true
# sni: example.com # aka server name
# alpn:
# - h2
# - http/1.1
# skip-cert-verify: true
*/
func (t Trojan) Identifier() string {
return net.JoinHostPort(t.Server, strconv.Itoa(t.Port)) + t.Password
}
func (t Trojan) String() string {
data, err := json.Marshal(t)
if err != nil {
return ""
}
return string(data)
}
func (t Trojan) ToClash() string {
data, err := json.Marshal(t)
if err != nil {
return ""
}
return "- " + string(data)
}
func (t Trojan) ToSurge() string {
return ""
}
func (t Trojan) Clone() Proxy {
return &t
}
// https://p4gefau1t.github.io/trojan-go/developer/url/
func (t Trojan) Link() (link string) {
query := url.Values{}
if t.SNI != "" {
query.Set("sni", url.QueryEscape(t.SNI))
}
uri := url.URL{
Scheme: "trojan",
User: url.User(url.QueryEscape(t.Password)),
Host: net.JoinHostPort(t.Server, strconv.Itoa(t.Port)),
RawQuery: query.Encode(),
Fragment: t.Name,
}
return uri.String()
}
func ParseTrojanLink(link string) (*Trojan, error) {
if !strings.HasPrefix(link, "trojan://") && !strings.HasPrefix(link, "trojan-go://") {
return nil, ErrorNotTrojanink
}
/**
trojan-go://
$(trojan-password)
@
trojan-host
:
port
/?
sni=$(tls-sni.com)&
type=$(original|ws|h2|h2+ws)&
host=$(websocket-host.com)&
path=$(/websocket/path)&
encryption=$(ss;aes-256-gcm;ss-password)&
plugin=$(...)
#$(descriptive-text)
*/
uri, err := url.Parse(link)
if err != nil {
return nil, ErrorNotSSLink
}
password := uri.User.Username()
password, _ = url.QueryUnescape(password)
server := uri.Hostname()
port, _ := strconv.Atoi(uri.Port())
moreInfos := uri.Query()
sni := moreInfos.Get("sni")
sni, _ = url.QueryUnescape(sni)
transformType := moreInfos.Get("type")
transformType, _ = url.QueryUnescape(transformType)
host := moreInfos.Get("host")
host, _ = url.QueryUnescape(host)
path := moreInfos.Get("path")
path, _ = url.QueryUnescape(path)
alpn := make([]string, 0)
if transformType == "h2" {
alpn = append(alpn, "h2")
}
if port == 0 {
return nil, ErrorNotTrojanink
}
return &Trojan{
Base: Base{
Name: strconv.Itoa(rand.Int()),
Server: server,
Port: port,
Type: "trojan",
},
Password: password,
ALPN: alpn,
UDP: true,
SNI: host,
SkipCertVerify: true,
}, nil
}
var (
trojanPlainRe = regexp.MustCompile("trojan(-go)?://([A-Za-z0-9+/_&?=@:%.-])+")
)
func GrepTrojanLinkFromString(text string) []string {
results := make([]string, 0)
texts := strings.Split(text, "trojan://")
for _, text := range texts {
results = append(results, trojanPlainRe.FindAllString("trojan://"+text, -1)...)
}
return results
}