mirror of
https://github.com/p4gefau1t/trojan-go.git
synced 2024-04-21 12:21:34 +00:00
add customizable protocol stack
This commit is contained in:
+2
-2
@@ -43,7 +43,7 @@ for PLATFORM in $PLATFORMS; do
|
||||
GOOS=${PLATFORM%/*}
|
||||
GOARCH=${PLATFORM#*/}
|
||||
ZIP_FILENAME="trojan-go-${GOOS}-${GOARCH}.zip"
|
||||
CMD="CGO_ENABLE=0 GOOS=${GOOS} GOARCH=${GOARCH} go build -tags \"full\" -o temp -ldflags=\"-s -w ${VAR_SETTING}\""
|
||||
CMD="CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} go build -tags \"full\" -o temp -ldflags=\"-s -w ${VAR_SETTING}\""
|
||||
echo "${CMD}"
|
||||
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"
|
||||
zip -j release/$ZIP_FILENAME temp/* ./*.dat
|
||||
@@ -59,7 +59,7 @@ for GOOS in $PLATFORMS_ARM; do
|
||||
GOARCH="arm"
|
||||
for GOARM in 7 6 5; do
|
||||
ZIP_FILENAME="trojan-go-${GOOS}-${GOARCH}v${GOARM}.zip"
|
||||
CMD="CGO_ENABLE=0 GOARM=${GOARM} GOOS=${GOOS} GOARCH=${GOARCH} go build -tags \"full\" -o temp -ldflags \"-s -w ${VAR_SETTING}\""
|
||||
CMD="CGO_ENABLED=0 GOARM=${GOARM} GOOS=${GOOS} GOARCH=${GOARCH} go build -tags \"full\" -o temp -ldflags \"-s -w ${VAR_SETTING}\""
|
||||
echo "${CMD}"
|
||||
eval "${CMD}" || FAILURES="${FAILURES} ${GOOS}/${GOARCH}v${GOARM}"
|
||||
zip -j release/$ZIP_FILENAME temp/* ./*.dat
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// +build custom full
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
_ "github.com/p4gefau1t/trojan-go/proxy/custom"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/adapter"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/http"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/mux"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/router"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/shadowsocks"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/simplesocks"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/socks"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/tls"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/transport"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/trojan"
|
||||
_ "github.com/p4gefau1t/trojan-go/tunnel/websocket"
|
||||
)
|
||||
+19
-4
@@ -7,11 +7,17 @@ import (
|
||||
"github.com/p4gefau1t/trojan-go/common"
|
||||
)
|
||||
|
||||
type TestStruct struct {
|
||||
type Foo struct {
|
||||
Field1 string `json,yaml:"field1"`
|
||||
Field2 bool `json:"field2" yaml:"field2"`
|
||||
}
|
||||
|
||||
type TestStruct struct {
|
||||
Field1 string `json,yaml:"field1"`
|
||||
Field2 bool `json,yaml:"field2"`
|
||||
Field3 []Foo `json,yaml:"field3"`
|
||||
}
|
||||
|
||||
func creator() interface{} {
|
||||
return &TestStruct{}
|
||||
}
|
||||
@@ -20,8 +26,14 @@ func TestJSONConfig(t *testing.T) {
|
||||
RegisterConfigCreator("test", creator)
|
||||
data := []byte(`
|
||||
{
|
||||
"Field1": "test1",
|
||||
"Field2": true
|
||||
"field1": "test1",
|
||||
"field2": true,
|
||||
"field3": [
|
||||
{
|
||||
"field1": "aaaa",
|
||||
"field2": true
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
ctx, err := WithJSONConfig(context.Background(), data)
|
||||
@@ -37,11 +49,14 @@ func TestYAMLConfig(t *testing.T) {
|
||||
data := []byte(`
|
||||
field1: 012345678
|
||||
field2: true
|
||||
field3:
|
||||
- field1: test
|
||||
field2: true
|
||||
`)
|
||||
ctx, err := WithYAMLConfig(context.Background(), data)
|
||||
common.Must(err)
|
||||
c := FromContext(ctx, "test").(*TestStruct)
|
||||
if c.Field1 != "012345678" || c.Field2 != true {
|
||||
if c.Field1 != "012345678" || c.Field2 != true || c.Field3[0].Field1 != "test" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@ import "github.com/p4gefau1t/trojan-go/config"
|
||||
|
||||
const Name = "CUSTOM"
|
||||
|
||||
type Node struct {
|
||||
Protocol string
|
||||
Tag string
|
||||
Config interface{}
|
||||
type NodeConfig struct {
|
||||
Protocol string `json,yaml:"protocol"`
|
||||
Tag string `json,yaml:"tag"`
|
||||
Config interface{} `json,yaml:"config"`
|
||||
}
|
||||
|
||||
type StackConfig struct {
|
||||
NodeList []Node `json,yaml:"node"`
|
||||
Path [][]string `json,yaml:"path"`
|
||||
Path [][]string `json,yaml:"path"`
|
||||
Node []NodeConfig `json,yaml:"node"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
||||
@@ -1 +1,126 @@
|
||||
package custom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/p4gefau1t/trojan-go/common"
|
||||
"github.com/p4gefau1t/trojan-go/config"
|
||||
"github.com/p4gefau1t/trojan-go/log"
|
||||
"github.com/p4gefau1t/trojan-go/proxy"
|
||||
"github.com/p4gefau1t/trojan-go/tunnel"
|
||||
"github.com/p4gefau1t/trojan-go/tunnel/freedom"
|
||||
"github.com/p4gefau1t/trojan-go/tunnel/http"
|
||||
"github.com/p4gefau1t/trojan-go/tunnel/simplesocks"
|
||||
"github.com/p4gefau1t/trojan-go/tunnel/socks"
|
||||
"github.com/p4gefau1t/trojan-go/tunnel/transport"
|
||||
"github.com/p4gefau1t/trojan-go/tunnel/trojan"
|
||||
"gopkg.in/yaml.v2"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func buildNodes(ctx context.Context, nodeConfigList []NodeConfig) (map[string]*proxy.Node, *proxy.Node, error) {
|
||||
nodes := make(map[string]*proxy.Node)
|
||||
var root *proxy.Node
|
||||
for _, nodeCfg := range nodeConfigList {
|
||||
nodeCfg.Protocol = strings.ToUpper(nodeCfg.Protocol)
|
||||
if _, err := tunnel.GetTunnel(nodeCfg.Protocol); err != nil {
|
||||
return nil, nil, common.NewError("invalid protocol name:" + nodeCfg.Protocol)
|
||||
}
|
||||
data, err := yaml.Marshal(nodeCfg.Config)
|
||||
if err != nil {
|
||||
return nil, nil, common.NewError("failed to parse config data for " + nodeCfg.Tag + " with protocol" + nodeCfg.Protocol).Base(err)
|
||||
}
|
||||
nodeContext, err := config.WithYAMLConfig(ctx, data)
|
||||
node := &proxy.Node{
|
||||
Name: nodeCfg.Protocol,
|
||||
Next: make(map[string]*proxy.Node),
|
||||
Context: nodeContext,
|
||||
}
|
||||
nodes[nodeCfg.Tag] = node
|
||||
if nodeCfg.Protocol == transport.Name || nodeCfg.Protocol == freedom.Name {
|
||||
if root != nil {
|
||||
return nil, nil, common.NewError("transport layer is defined for twice")
|
||||
}
|
||||
log.Debug("root found:" + nodeCfg.Tag)
|
||||
root = node
|
||||
}
|
||||
}
|
||||
if root == nil {
|
||||
return nil, nil, common.NewError("no transport layer found")
|
||||
}
|
||||
return nodes, root, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterProxyCreator(Name, func(ctx context.Context) (*proxy.Proxy, error) {
|
||||
cfg := config.FromContext(ctx, Name).(*Config)
|
||||
|
||||
// inbound
|
||||
nodes, root, err := buildNodes(ctx, cfg.Inbound.Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transportServer, err := transport.NewServer(root.Context, nil)
|
||||
if err != nil {
|
||||
return nil, common.NewError("failed to initialize transport server").Base(err)
|
||||
}
|
||||
root.Server = transportServer
|
||||
|
||||
// build server tree
|
||||
for _, path := range cfg.Inbound.Path {
|
||||
lastNode := root
|
||||
for i, tag := range path {
|
||||
if _, found := nodes[tag]; !found {
|
||||
return nil, common.NewError("invalid node tag: " + tag)
|
||||
}
|
||||
if i == len(path)-1 {
|
||||
switch nodes[tag].Name {
|
||||
case trojan.Name, simplesocks.Name, socks.Name, http.Name:
|
||||
default:
|
||||
return nil, common.NewError("inbound path must end with protocol trojan/simplesocks/http/socks")
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
if nodes[tag].Name != transport.Name {
|
||||
return nil, common.NewError("inbound path must start with protocol transport")
|
||||
}
|
||||
continue
|
||||
}
|
||||
lastNode = lastNode.LinkNextNode(nodes[tag])
|
||||
}
|
||||
lastNode.IsEndpoint = true
|
||||
}
|
||||
|
||||
servers := proxy.FindAllEndpoints(root)
|
||||
|
||||
if len(cfg.Outbound.Path) != 1 {
|
||||
return nil, common.NewError("there must be only 1 path for outbound protocol stack")
|
||||
}
|
||||
|
||||
// outbound
|
||||
nodes, _, err = buildNodes(ctx, cfg.Outbound.Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// build client stack
|
||||
var client tunnel.Client
|
||||
for i, tag := range cfg.Outbound.Path[0] {
|
||||
if _, found := nodes[tag]; !found {
|
||||
return nil, common.NewError("invalid node tag: " + tag)
|
||||
}
|
||||
if i == 0 && nodes[tag].Name != freedom.Name && nodes[tag].Name != transport.Name {
|
||||
return nil, common.NewError("outbound path must start with protocol freedom/transport")
|
||||
}
|
||||
t, err := tunnel.GetTunnel(nodes[tag].Name)
|
||||
if err != nil {
|
||||
return nil, common.NewError("invalid tunnel name").Base(err)
|
||||
}
|
||||
client, err = t.NewClient(nodes[tag].Context, client)
|
||||
if err != nil {
|
||||
return nil, common.NewError("failed to create client").Base(err)
|
||||
}
|
||||
}
|
||||
return proxy.NewProxy(ctx, servers, client), nil
|
||||
})
|
||||
}
|
||||
|
||||
+18
-38
@@ -12,16 +12,7 @@ type Node struct {
|
||||
IsEndpoint bool
|
||||
context.Context
|
||||
tunnel.Server
|
||||
}
|
||||
|
||||
func NewNode(name string, isEndpoint bool, context context.Context, server tunnel.Server) *Node {
|
||||
return &Node{
|
||||
Name: name,
|
||||
IsEndpoint: isEndpoint,
|
||||
Context: context,
|
||||
Server: server,
|
||||
Next: make(map[string]*Node),
|
||||
}
|
||||
tunnel.Client
|
||||
}
|
||||
|
||||
func (n *Node) BuildNext(name string) *Node {
|
||||
@@ -46,6 +37,23 @@ func (n *Node) BuildNext(name string) *Node {
|
||||
return newNode
|
||||
}
|
||||
|
||||
func (n *Node) LinkNextNode(next *Node) *Node {
|
||||
if next, found := n.Next[next.Name]; found {
|
||||
return next
|
||||
}
|
||||
n.Next[next.Name] = next
|
||||
t, err := tunnel.GetTunnel(next.Name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s, err := t.NewServer(next.Context, n.Server) // context of the child nodes have been initialized
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
next.Server = s
|
||||
return next
|
||||
}
|
||||
|
||||
func FindAllEndpoints(root *Node) []tunnel.Server {
|
||||
list := make([]tunnel.Server, 0)
|
||||
if root.IsEndpoint || len(root.Next) == 0 {
|
||||
@@ -57,34 +65,6 @@ func FindAllEndpoints(root *Node) []tunnel.Server {
|
||||
return list
|
||||
}
|
||||
|
||||
func buildServerStacksTree(ctx context.Context, current *Node, parent *Node) ([]tunnel.Server, error) {
|
||||
t, err := tunnel.GetTunnel(current.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current.Server, err = t.NewServer(ctx, parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
leaves := make([]tunnel.Server, 0)
|
||||
for _, child := range current.Next {
|
||||
subTreeLeaves, err := buildServerStacksTree(ctx, child, current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
leaves = append(leaves, subTreeLeaves...)
|
||||
}
|
||||
// current node is a leave node
|
||||
if len(leaves) == 0 || current.IsEndpoint {
|
||||
leaves = append(leaves, current)
|
||||
}
|
||||
return leaves, nil
|
||||
}
|
||||
|
||||
func CreateServersStacksTree(ctx context.Context, root *Node) ([]tunnel.Server, error) {
|
||||
return buildServerStacksTree(ctx, root, nil)
|
||||
}
|
||||
|
||||
// CreateClientStack create client tunnel stacks from lists
|
||||
func CreateClientStack(ctx context.Context, clientStack []string) (tunnel.Client, error) {
|
||||
var client tunnel.Client
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/p4gefau1t/trojan-go/common"
|
||||
_ "github.com/p4gefau1t/trojan-go/proxy/custom"
|
||||
"github.com/p4gefau1t/trojan-go/test/util"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCustom(t *testing.T) {
|
||||
serverPort := common.PickPort("tcp", "127.0.0.1")
|
||||
socksPort := common.PickPort("tcp", "127.0.0.1")
|
||||
clientData := fmt.Sprintf(`
|
||||
run-type: custom
|
||||
|
||||
inbound:
|
||||
node:
|
||||
- protocol: transport
|
||||
tag: transport
|
||||
config:
|
||||
local-addr: 127.0.0.1
|
||||
local-port: %d
|
||||
- protocol: socks
|
||||
tag: socks
|
||||
path:
|
||||
-
|
||||
- transport
|
||||
- socks
|
||||
|
||||
outbound:
|
||||
node:
|
||||
- protocol: transport
|
||||
tag: transport
|
||||
config:
|
||||
remote-addr: 127.0.0.1
|
||||
remote-port: %d
|
||||
|
||||
- protocol: tls
|
||||
tag: tls
|
||||
config:
|
||||
ssl:
|
||||
sni: localhost
|
||||
key: server.key
|
||||
cert: server.crt
|
||||
|
||||
- protocol: trojan
|
||||
tag: trojan
|
||||
config:
|
||||
password:
|
||||
- 12345678
|
||||
|
||||
path:
|
||||
-
|
||||
- transport
|
||||
- tls
|
||||
- trojan
|
||||
|
||||
`, socksPort, serverPort)
|
||||
serverData := fmt.Sprintf(`
|
||||
run-type: custom
|
||||
|
||||
inbound:
|
||||
node:
|
||||
- protocol: transport
|
||||
tag: transport
|
||||
config:
|
||||
local-addr: 127.0.0.1
|
||||
local-port: %d
|
||||
remote-addr: 127.0.0.1
|
||||
remote-port: %s
|
||||
|
||||
- protocol: tls
|
||||
tag: tls
|
||||
config:
|
||||
ssl:
|
||||
sni: localhost
|
||||
key: server.key
|
||||
cert: server.crt
|
||||
|
||||
- protocol: trojan
|
||||
tag: trojan
|
||||
config:
|
||||
disable-http-check: true
|
||||
password:
|
||||
- 12345678
|
||||
|
||||
path:
|
||||
-
|
||||
- transport
|
||||
- tls
|
||||
- trojan
|
||||
|
||||
outbound:
|
||||
node:
|
||||
- protocol: freedom
|
||||
tag: freedom
|
||||
|
||||
path:
|
||||
-
|
||||
- freedom
|
||||
|
||||
`, serverPort, util.HTTPPort)
|
||||
|
||||
if !CheckClientServer(clientData, serverData, socksPort) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/p4gefau1t/trojan-go/tunnel"
|
||||
)
|
||||
|
||||
const Name = "RAW"
|
||||
const Name = "FREEDOM"
|
||||
|
||||
type Tunnel struct{}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user