mirror of
https://github.com/p4gefau1t/trojan-go.git
synced 2024-04-21 12:21:34 +00:00
331 lines
9.2 KiB
Go
331 lines
9.2 KiB
Go
package conf
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/sha256"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/p4gefau1t/trojan-go/common"
|
|
"github.com/p4gefau1t/trojan-go/log"
|
|
"golang.org/x/crypto/pbkdf2"
|
|
)
|
|
|
|
func loadCommonConfig(config *GlobalConfig) error {
|
|
//log level
|
|
log.SetLogLevel(log.LogLevel(config.LogLevel))
|
|
|
|
//buffer size, 4KiB to 4MB
|
|
if config.BufferSize < 4 || config.BufferSize > 4096 {
|
|
return common.NewError("invalid buffer size, 4 < buffer_size < 4096")
|
|
}
|
|
|
|
config.BufferSize *= 1024
|
|
|
|
//password settings
|
|
if len(config.Passwords) == 0 {
|
|
if config.RunType == Client {
|
|
return common.NewError("no password found")
|
|
}
|
|
log.Warn("password is not specified in config file")
|
|
}
|
|
config.Hash = make(map[string]string)
|
|
for _, password := range config.Passwords {
|
|
config.Hash[common.SHA224String(password)] = password
|
|
}
|
|
|
|
//address settings
|
|
config.LocalAddress = common.NewAddress(config.LocalHost, config.LocalPort, "tcp")
|
|
config.RemoteAddress = common.NewAddress(config.RemoteHost, config.RemotePort, "tcp")
|
|
config.TargetAddress = common.NewAddress(config.TargetHost, config.TargetPort, "tcp")
|
|
|
|
if config.TLS.FallbackPort != 0 {
|
|
config.TLS.FallbackAddress = common.NewAddress(config.RemoteHost, config.TLS.FallbackPort, "tcp")
|
|
}
|
|
|
|
//api settings
|
|
if config.API.Enabled {
|
|
config.API.APIAddress = common.NewAddress(config.API.APIHost, config.API.APIPort, "tcp")
|
|
}
|
|
|
|
//tls settings
|
|
if config.TLS.Cipher != "" || config.TLS.CipherTLS13 != "" {
|
|
specifiedSuites := strings.Split(config.TLS.Cipher+":"+config.TLS.CipherTLS13, ":")
|
|
supportedSuites := tls.CipherSuites()
|
|
invalid := false
|
|
for _, specified := range specifiedSuites {
|
|
found := false
|
|
if specified == "" {
|
|
continue
|
|
}
|
|
for _, supported := range supportedSuites {
|
|
if supported.Name == specified {
|
|
config.TLS.CipherSuites = append(config.TLS.CipherSuites, supported.ID)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
invalid = true
|
|
log.Warn("found invalid cipher name", specified)
|
|
break
|
|
}
|
|
}
|
|
if invalid && len(supportedSuites) >= 1 {
|
|
log.Warn("cipher list contains invalid cipher name, ignored")
|
|
log.Warn("here's a list of supported ciphers:")
|
|
list := ""
|
|
for _, c := range supportedSuites {
|
|
list += c.Name + ":"
|
|
}
|
|
log.Warn(list[0 : len(list)-1])
|
|
config.TLS.CipherSuites = nil
|
|
}
|
|
} else {
|
|
config.TLS.CipherSuites = nil
|
|
}
|
|
|
|
//websocket settings
|
|
if config.Websocket.Enabled {
|
|
log.Info("websocket enabled")
|
|
if config.Websocket.Path == "" {
|
|
return common.NewError("websocket path is empty")
|
|
}
|
|
if config.Websocket.Path[0] != '/' {
|
|
return common.NewError("websocket path must start with \"/\"")
|
|
}
|
|
if config.RunType == Client && config.Websocket.HostName == "" {
|
|
log.Warn("Client websocket host_name is unspecified, using remote_addr \"", config.RemoteHost, "\" as host_name")
|
|
config.Websocket.HostName = config.RemoteHost
|
|
if ip := net.ParseIP(config.RemoteHost); ip != nil && ip.To4() == nil { //ipv6 address
|
|
config.Websocket.HostName = "[" + config.RemoteHost + "]"
|
|
}
|
|
}
|
|
if config.Websocket.ObfuscationPassword != "" {
|
|
log.Info("websocket obfs enabled")
|
|
password := []byte(config.Websocket.ObfuscationPassword)
|
|
salt := []byte{48, 149, 6, 18, 13, 193, 247, 116, 197, 135, 236, 175, 190, 209, 146, 48}
|
|
config.Websocket.ObfuscationKey = pbkdf2.Key(password, salt, 32, aes.BlockSize, sha256.New)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadClientConfig(config *GlobalConfig) error {
|
|
var err error
|
|
|
|
//router settings
|
|
config.Router.BlockList = []byte{}
|
|
config.Router.ProxyList = []byte{}
|
|
config.Router.BypassList = []byte{}
|
|
|
|
for _, s := range config.Router.Block {
|
|
if strings.HasPrefix(s, "geoip:") {
|
|
config.Router.BlockIPCode = append(config.Router.BlockIPCode, s[len("geoip:"):])
|
|
continue
|
|
}
|
|
if strings.HasPrefix(s, "geosite:") {
|
|
config.Router.BlockSiteCode = append(config.Router.BlockSiteCode, s[len("geosite:"):])
|
|
continue
|
|
}
|
|
data, err := ioutil.ReadFile(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
config.Router.BlockList = append(config.Router.BlockList, data...)
|
|
config.Router.BlockList = append(config.Router.BlockList, byte('\n'))
|
|
}
|
|
|
|
for _, s := range config.Router.Bypass {
|
|
if strings.HasPrefix(s, "geoip:") {
|
|
config.Router.BypassIPCode = append(config.Router.BypassIPCode, s[len("geoip:"):])
|
|
continue
|
|
}
|
|
if strings.HasPrefix(s, "geosite:") {
|
|
config.Router.BypassSiteCode = append(config.Router.BypassSiteCode, s[len("geosite:"):])
|
|
continue
|
|
}
|
|
data, err := ioutil.ReadFile(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
config.Router.BypassList = append(config.Router.BypassList, data...)
|
|
config.Router.BypassList = append(config.Router.BypassList, byte('\n'))
|
|
}
|
|
|
|
for _, s := range config.Router.Proxy {
|
|
if strings.HasPrefix(s, "geoip:") {
|
|
config.Router.ProxyIPCode = append(config.Router.ProxyIPCode, s[len("geoip:"):])
|
|
continue
|
|
}
|
|
if strings.HasPrefix(s, "geosite:") {
|
|
config.Router.ProxySiteCode = append(config.Router.ProxySiteCode, s[len("geosite:"):])
|
|
continue
|
|
}
|
|
data, err := ioutil.ReadFile(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
config.Router.ProxyList = append(config.Router.ProxyList, data...)
|
|
config.Router.ProxyList = append(config.Router.ProxyList, byte('\n'))
|
|
}
|
|
|
|
config.Router.GeoIP, err = ioutil.ReadFile(config.Router.GeoIPFilename)
|
|
if err != nil {
|
|
config.Router.GeoIP = []byte{}
|
|
log.Warn(err)
|
|
}
|
|
config.Router.GeoSite, err = ioutil.ReadFile(config.Router.GeoSiteFilename)
|
|
if err != nil {
|
|
config.Router.GeoSite = []byte{}
|
|
log.Warn(err)
|
|
}
|
|
|
|
//tls settings
|
|
if config.TLS.SNI == "" {
|
|
log.Warn("SNI is unspecified, using remote_addr as SNI")
|
|
config.TLS.SNI = config.RemoteHost
|
|
}
|
|
if config.TLS.CertPath == "" {
|
|
log.Info("cert of the remote server is not specified, using default CA list")
|
|
return nil
|
|
}
|
|
|
|
caCertByte, err := ioutil.ReadFile(config.TLS.CertPath)
|
|
if err != nil {
|
|
return common.NewError("failed to load cert file").Base(err)
|
|
}
|
|
pool := x509.NewCertPool()
|
|
ok := pool.AppendCertsFromPEM(caCertByte)
|
|
if !ok {
|
|
log.Warn("invalid CA cert list")
|
|
}
|
|
log.Info("using custom CA list")
|
|
pemCerts := caCertByte
|
|
for len(pemCerts) > 0 {
|
|
config.TLS.CertPool = pool
|
|
var block *pem.Block
|
|
block, pemCerts = pem.Decode(pemCerts)
|
|
if block == nil {
|
|
break
|
|
}
|
|
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
|
continue
|
|
}
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
log.Debug("issuer:", cert.Issuer, ", subject:", cert.Subject)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func loadServerConfig(config *GlobalConfig) error {
|
|
|
|
//check web server
|
|
resp, err := http.Get("http://" + config.RemoteAddress.String())
|
|
if err != nil {
|
|
return common.NewError(config.RemoteAddress.String() + " is not a valid web server").Base(err)
|
|
}
|
|
buf := [128]byte{}
|
|
_, err = resp.Body.Read(buf[:])
|
|
log.Debug("body:\n" + string(buf[:]))
|
|
resp.Body.Close()
|
|
|
|
if config.TLS.KeyPassword != "" {
|
|
keyFile, err := ioutil.ReadFile(config.TLS.KeyPath)
|
|
if err != nil {
|
|
return common.NewError("failed to load key file").Base(err)
|
|
}
|
|
keyBlock, _ := pem.Decode(keyFile)
|
|
if keyBlock == nil {
|
|
return common.NewError("failed to decode key file").Base(err)
|
|
}
|
|
decryptedKey, err := x509.DecryptPEMBlock(keyBlock, []byte(config.TLS.KeyPassword))
|
|
if err == nil {
|
|
return common.NewError("failed to decrypt key").Base(err)
|
|
}
|
|
|
|
certFile, err := ioutil.ReadFile(config.TLS.CertPath)
|
|
certBlock, _ := pem.Decode(certFile)
|
|
if certBlock == nil {
|
|
return common.NewError("failed to decode cert file").Base(err)
|
|
}
|
|
|
|
keyPair, err := tls.X509KeyPair(certBlock.Bytes, decryptedKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
config.TLS.KeyPair = []tls.Certificate{keyPair}
|
|
} else {
|
|
keyPair, err := tls.LoadX509KeyPair(config.TLS.CertPath, config.TLS.KeyPath)
|
|
if err != nil {
|
|
return common.NewError("failed to load key pair").Base(err)
|
|
}
|
|
config.TLS.KeyPair = []tls.Certificate{keyPair}
|
|
}
|
|
if config.TLS.HTTPFile != "" {
|
|
payload, err := ioutil.ReadFile(config.TLS.HTTPFile)
|
|
if err != nil {
|
|
log.Warn("failed to load http response file", err)
|
|
}
|
|
config.TLS.HTTPResponse = payload
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ParseJSON(data []byte) (*GlobalConfig, error) {
|
|
config := &GlobalConfig{}
|
|
|
|
//default settings
|
|
config.LogLevel = 1
|
|
config.BufferSize = 512
|
|
config.TLS.Verify = true
|
|
config.TLS.VerifyHostname = true
|
|
config.TLS.SessionTicket = true
|
|
config.TLS.ReuseSession = true
|
|
config.Mux.IdleTimeout = 60
|
|
config.Mux.Concurrency = 8
|
|
config.MySQL.CheckRate = 60
|
|
config.Router.DefaultPolicy = "proxy"
|
|
config.Router.GeoIPFilename = common.GetProgramDir() + "/geoip.dat"
|
|
config.Router.GeoSiteFilename = common.GetProgramDir() + "/geosite.dat"
|
|
config.Websocket.DoubleTLS = true
|
|
config.Websocket.DoubleTLSVerify = true
|
|
config.Websocket.ObfuscationPassword = ""
|
|
|
|
err := json.Unmarshal(data, config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := loadCommonConfig(config); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch config.RunType {
|
|
case Client, NAT, Forward:
|
|
if err := loadClientConfig(config); err != nil {
|
|
return nil, err
|
|
}
|
|
case Server:
|
|
if err := loadServerConfig(config); err != nil {
|
|
return nil, err
|
|
}
|
|
case Relay:
|
|
default:
|
|
return nil, common.NewError("invalid run type:" + string(config.RunType))
|
|
}
|
|
|
|
return config, nil
|
|
}
|