Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Commit

Permalink
add trojan support
Browse files Browse the repository at this point in the history
  • Loading branch information
zu1k committed Aug 20, 2020
1 parent 023de15 commit 097c2d2
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
2 changes: 2 additions & 0 deletions pkg/getter/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func String2Proxy(link string) proxy.Proxy {
data, err = proxy.ParseVmessLink(link)
} else if strings.HasPrefix(link, "ss://") {
data, err = proxy.ParseSSLink(link)
} else if strings.HasPrefix(link, "trojan://") {
data, err = proxy.ParseTrojanLink(link)
}
if err != nil {
return nil
Expand Down
147 changes: 147 additions & 0 deletions pkg/proxy/trojan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
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
}

func ParseTrojanLink(link string) (*Trojan, error) {
if !strings.HasPrefix(link, "trojan://") || !strings.HasPrefix(link, "trojan-go://") {
return nil, ErrorNotSSRLink
}

/**
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, ssPlainRe.FindAllString("trojan://"+text, -1)...)
}
return results
}

0 comments on commit 097c2d2

Please sign in to comment.