Skip to content

Commit

Permalink
add customizable protocol stack
Browse files Browse the repository at this point in the history
  • Loading branch information
p4gefau1t committed Jun 19, 2020
1 parent 66514cb commit 9fd62bb
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 51 deletions.
4 changes: 2 additions & 2 deletions build-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
18 changes: 18 additions & 0 deletions build/custom.go
Original file line number Diff line number Diff line change
@@ -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"
)
23 changes: 19 additions & 4 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
}
Expand All @@ -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)
Expand All @@ -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()
}
}
12 changes: 6 additions & 6 deletions proxy/custom/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
125 changes: 125 additions & 0 deletions proxy/custom/custom.go
Original file line number Diff line number Diff line change
@@ -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
})
}
56 changes: 18 additions & 38 deletions proxy/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
Expand Down
Loading

0 comments on commit 9fd62bb

Please sign in to comment.