add customizable protocol stack

This commit is contained in:
Page Fault
2020-06-19 16:40:22 +00:00
parent 66514cb12e
commit 9fd62bb38c
9 changed files with 297 additions and 51 deletions
+2 -2
View File
@@ -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
+18
View File
@@ -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
View File
@@ -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()
}
}
+6 -6
View File
@@ -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 {
+125
View File
@@ -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
View File
@@ -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
+108
View File
@@ -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()
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/p4gefau1t/trojan-go/tunnel"
)
const Name = "RAW"
const Name = "FREEDOM"
type Tunnel struct{}