glider/proxy/trojan/trojan.go

87 lines
1.8 KiB
Go

// protocol spec:
// https://trojan-gfw.github.io/trojan/protocol
package trojan
import (
"crypto/sha256"
"crypto/tls"
"encoding/hex"
"errors"
"fmt"
"net"
"net/url"
"strings"
"github.com/nadoo/glider/proxy"
)
// Trojan is a base trojan struct.
type Trojan struct {
dialer proxy.Dialer
proxy proxy.Proxy
addr string
pass [56]byte
withTLS bool
tlsConfig *tls.Config
serverName string
skipVerify bool
certFile string
keyFile string
fallback string
}
// NewTrojan returns a trojan proxy.
func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
u, err := url.Parse(s)
if err != nil {
return nil, fmt.Errorf("parse url err: %s", err)
}
query := u.Query()
t := &Trojan{
dialer: d,
proxy: p,
addr: u.Host,
withTLS: true,
skipVerify: query.Get("skipVerify") == "true",
serverName: query.Get("serverName"),
certFile: query.Get("cert"),
keyFile: query.Get("key"),
fallback: query.Get("fallback"),
}
if t.addr != "" {
if _, port, _ := net.SplitHostPort(t.addr); port == "" {
t.addr = net.JoinHostPort(t.addr, "443")
}
if t.serverName == "" {
t.serverName = t.addr[:strings.LastIndex(t.addr, ":")]
}
}
// pass
pass := u.User.Username()
if pass == "" {
return nil, errors.New("[trojan] password must be specified")
}
hash := sha256.New224()
hash.Write([]byte(pass))
hex.Encode(t.pass[:], hash.Sum(nil))
return t, nil
}
func init() {
proxy.AddUsage("trojan", `
Trojan client scheme:
trojan://pass@host:port[?serverName=SERVERNAME][&skipVerify=true][&cert=PATH]
trojanc://pass@host:port (cleartext, without TLS)
Trojan server scheme:
trojan://pass@host:port?cert=PATH&key=PATH[&fallback=127.0.0.1]
trojanc://pass@host:port[?fallback=127.0.0.1] (cleartext, without TLS)
`)
}