summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--cmd/tunneld/main.go106
-rw-r--r--init/tunnel@.service10
-rw-r--r--pkg/http/http.go2
-rw-r--r--pkg/server/echo.go14
-rw-r--r--pkg/server/env/env.go54
-rw-r--r--pkg/server/hook/aes.go2
-rw-r--r--pkg/server/hook/auth.go2
-rw-r--r--pkg/server/hook/dump.go2
-rw-r--r--pkg/server/hook/proxy.go4
-rw-r--r--pkg/server/server.go27
-rw-r--r--pkg/server/socket/dial.go2
-rw-r--r--pkg/server/socket/proxy.go2
-rwxr-xr-xtest/env.sh5
14 files changed, 151 insertions, 84 deletions
diff --git a/TODO b/TODO
index 2a4a7f9..852b397 100644
--- a/TODO
+++ b/TODO
@@ -15,4 +15,5 @@
- dial with timeout
- proxy socket with connect wait timeout
-- config split with double spaces
+- fix: set a @[a]
+- info for proxy
diff --git a/cmd/tunneld/main.go b/cmd/tunneld/main.go
index a0c0e03..908fcc3 100644
--- a/cmd/tunneld/main.go
+++ b/cmd/tunneld/main.go
@@ -13,6 +13,7 @@ import (
"os/signal"
"os/user"
"path"
+ "regexp"
"strconv"
"strings"
"syscall"
@@ -28,6 +29,10 @@ var (
socketFlag = flag.String("S", "", "path to control socket")
)
+const trimSize = 32
+
+var wordsRe = regexp.MustCompile("[[:^space:]]+")
+
func initLog() {
var logFlags int
@@ -85,56 +90,81 @@ func getSocketPath() string {
return s
}
-func openConfig() (*os.File, error) {
- var c string
+type parser struct {
+ s *server.Server
+ m map[string]bool
+ f []string
+}
- if *configFlag != "" {
- c = *configFlag
- } else {
- c = config.GetConfigPath()
- }
+func newParser(s *server.Server) *parser {
+ return &parser{s: s, m: map[string]bool{}}
+}
- if c == "" {
- return nil, nil
+func (p *parser) parse(file string, skip bool) error {
+ if p.m[file] {
+ log.Printf("skip reading '%s' twice", file)
+ return nil
}
- fp, err := os.Open(c)
- if err != nil {
- if *configFlag == "" && errors.Is(err, syscall.ENOENT) {
- return nil, nil
- }
+ p.m[file] = true
+ p.f = append(p.f, file)
- return nil, err
- }
+ defer func() {
+ p.f = p.f[:len(p.f)-1]
+ }()
- return fp, nil
-}
+ log.Println("read config", file)
-func readConfig(s *server.Server) error {
- fp, err := openConfig()
- if fp == nil || err != nil {
+ fp, err := os.Open(file)
+ if err != nil {
+ if skip && errors.Is(err, syscall.ENOENT) {
+ return nil
+ }
return err
}
defer fp.Close()
+ return p.read(fp)
+}
+
+func (p *parser) read(fp *os.File) error {
scanner := bufio.NewScanner(fp)
- scanner.Split(bufio.ScanLines)
for nline := 1; scanner.Scan(); nline++ {
- args := strings.SplitN(scanner.Text(), "#", 2)
- cmd := strings.TrimSpace(args[0])
+ s := strings.SplitN(scanner.Text(), "#", 2)
+ t := strings.TrimSpace(s[0])
+ args := wordsRe.FindAllString(s[0], -1)
- if cmd == "" {
- continue
+ if err := p.apply(args); err != nil {
+ return fmt.Errorf("%s:%d: %s: %w", fp.Name(), nline, t, err)
}
+ }
- if err := s.Command(cmd); err != nil {
- return fmt.Errorf("%s:%d: %s: %w", fp.Name(), nline, cmd, err)
+ return nil
+}
+
+func (p *parser) apply(args []string) error {
+ if len(args) == 0 {
+ return nil
+ }
+
+ if args[0] == "include" {
+ if len(args) < 2 {
+ return errors.New("argument expected")
}
+
+ file := args[1]
+
+ if !path.IsAbs(file) {
+ now := p.f[len(p.f)-1]
+ file = path.Join(path.Dir(now), file)
+ }
+
+ return p.parse(file, false)
}
- return nil
+ return p.s.Command(args)
}
func updateSocketGroup(s *server.Server, group string) error {
@@ -173,7 +203,7 @@ func updateSocketGroup(s *server.Server, group string) error {
}
func runCommand(s *server.Server, name string) error {
- cmd := s.Env().Get(name)
+ cmd := s.Env().Value(name)
if cmd == "" {
return nil
}
@@ -209,10 +239,24 @@ func runCommand(s *server.Server, name string) error {
}
func configure(s *server.Server) error {
- if err := readConfig(s); err != nil {
+ var file string
+ var skip bool
+
+ if *configFlag != "" {
+ file = *configFlag
+ } else {
+ file = config.GetConfigPath()
+ skip = true
+ }
+
+ if err := newParser(s).parse(file, skip); err != nil {
return err
}
+ return postconfigure(s)
+}
+
+func postconfigure(s *server.Server) error {
if group, ok := s.Env().Find("server.socket.group"); ok {
if err := updateSocketGroup(s, group); err != nil {
return err
diff --git a/init/tunnel@.service b/init/tunnel@.service
new file mode 100644
index 0000000..031e6a0
--- /dev/null
+++ b/init/tunnel@.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=Tunnel Daemon
+After=network.target
+
+[Service]
+ExecStart=tunneld -c /etc/tunnel/%I.conf
+KillMode=process
+
+[Install]
+WantedBy=multi-user.target
diff --git a/pkg/http/http.go b/pkg/http/http.go
index 4180c81..04e26ef 100644
--- a/pkg/http/http.go
+++ b/pkg/http/http.go
@@ -77,7 +77,7 @@ func parse(r *bufio.Reader, init handler, f handler) error {
}
if line == "" {
- return nil
+ break
}
if err := f(line); err != nil {
diff --git a/pkg/server/echo.go b/pkg/server/echo.go
index 0387a3e..2ac54b9 100644
--- a/pkg/server/echo.go
+++ b/pkg/server/echo.go
@@ -5,7 +5,19 @@ import (
)
func echo(r *request) {
- r.Print(strings.Join(r.args, " "))
+ expand := false
+ args := r.args
+
+ if r.argc > 0 && r.args[0] == "-e" {
+ expand = true
+ args = args[1:]
+ }
+
+ s := strings.Join(args, " ")
+ if expand {
+ s = r.c.s.env.Expand(s)
+ }
+ r.Print(s)
}
func init() {
diff --git a/pkg/server/env/env.go b/pkg/server/env/env.go
index 8594e10..98ee231 100644
--- a/pkg/server/env/env.go
+++ b/pkg/server/env/env.go
@@ -20,7 +20,8 @@ type Env struct {
const namePattern = "[a-zA-Z][a-zA-Z0-9.]*"
var isGoodName = regexp.MustCompile("^" + namePattern + "$").MatchString
-var varRe = regexp.MustCompile("@(" + namePattern + "|{" + namePattern + "})")
+var varexpRe = regexp.MustCompile("@(" + namePattern + "|{" + namePattern + "})")
+var expandRe = regexp.MustCompile("@[" + namePattern + "]")
var errBadVariable = errors.New("bad variable name")
var errEmptyVariable = errors.New("empty variable")
@@ -67,6 +68,11 @@ func (e Env) Get(key string) string {
return v
}
+func (e Env) Has(key string) bool {
+ _, ok := e.Find(key)
+ return ok
+}
+
func (e Env) Set(key string, value string) error {
if !isGoodName(key) {
return errBadVariable
@@ -129,17 +135,9 @@ func (e *env) Clear() {
e.m = nil
}
-func (e Env) Eval(s string) string {
- repl := func(v string) string {
- key := v[1:]
- if key[0] == '{' {
- key = key[1 : len(key)-1]
- }
- return e.Get(key)
- }
-
+func (e Env) replaceWith(r *regexp.Regexp, s string, f func(string) string) string {
for {
- t := varRe.ReplaceAllStringFunc(s, repl)
+ t := r.ReplaceAllStringFunc(s, f)
if t == s {
break
@@ -151,10 +149,36 @@ func (e Env) Eval(s string) string {
return s
}
-func (e Env) GetLocal(key string) string {
- if v := e.Eval("@{tunnel.@{tunnel}." + key + "}"); v != "" {
- return v
+func (e Env) Eval(s string) string {
+ return e.replaceWith(varexpRe, s, func(v string) string {
+ key := v[1:]
+ if key[0] == '{' {
+ key = key[1 : len(key)-1]
+ }
+ return e.Get(key)
+ })
+}
+
+func (e Env) EvalStrings(s []string) []string {
+ t := make([]string, len(s))
+ for n, v := range s {
+ t[n] = e.Eval(v)
+ }
+ return t
+}
+
+func (e Env) Expand(s string) string {
+ return e.replaceWith(expandRe, s, func(v string) string {
+ return e.Get(v[2 : len(v)-1])
+ })
+}
+
+func (e Env) Value(key string) string {
+ if e.Has("tunnel") {
+ if v := e.Expand("@[tunnel.@[tunnel]." + key + "]"); v != "" {
+ return v
+ }
}
- return e.Get(key)
+ return e.Expand(e.Get(key))
}
diff --git a/pkg/server/hook/aes.go b/pkg/server/hook/aes.go
index ef0ef1a..e437651 100644
--- a/pkg/server/hook/aes.go
+++ b/pkg/server/hook/aes.go
@@ -65,7 +65,7 @@ func (a *aesPipe) Recv(rq, wq queue.Q) error {
}
func newAes(env env.Env) *aesPipe {
- s := env.GetLocal("secret")
+ s := env.Value("secret")
h := md5.Sum([]byte(s))
a := &aesPipe{key: make([]byte, 16)}
diff --git a/pkg/server/hook/auth.go b/pkg/server/hook/auth.go
index 86acd91..a30e257 100644
--- a/pkg/server/hook/auth.go
+++ b/pkg/server/hook/auth.go
@@ -151,7 +151,7 @@ func (a *auth) Recv(rq, wq queue.Q) error {
func (h *authHook) Open(env env.Env) (interface{}, error) {
a := &auth{
h: h,
- secret: env.GetLocal("secret"),
+ secret: env.Value("secret"),
recvChallenge: make(chan struct{}),
recvHash: make(chan struct{}),
fail: make(chan struct{}),
diff --git a/pkg/server/hook/dump.go b/pkg/server/hook/dump.go
index d6bcd90..864443b 100644
--- a/pkg/server/hook/dump.go
+++ b/pkg/server/hook/dump.go
@@ -73,7 +73,7 @@ func (h *dumpHook) where(env env.Env) string {
return h.file
}
- if v := env.GetLocal("dump.file"); v != "" {
+ if v := env.Value("dump.file"); v != "" {
return v
}
diff --git a/pkg/server/hook/proxy.go b/pkg/server/hook/proxy.go
index a488f58..7da4c4a 100644
--- a/pkg/server/hook/proxy.go
+++ b/pkg/server/hook/proxy.go
@@ -65,7 +65,7 @@ func (p *proxy) Recv(rq, wq queue.Q) error {
}
func (h *proxyHook) Open(env env.Env) (interface{}, error) {
- addr := env.Eval(h.addr)
+ addr := env.Expand(h.addr)
if !isGoodAddr(addr) {
return nil, fmt.Errorf("invalid addr '%s'", addr)
}
@@ -77,7 +77,7 @@ func (h *proxyHook) Open(env env.Env) (interface{}, error) {
}
if p.auth == "" {
- p.auth = env.GetLocal("proxy.auth")
+ p.auth = env.Value("proxy.auth")
}
return p, nil
diff --git a/pkg/server/server.go b/pkg/server/server.go
index c389d50..5ca81d1 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -7,7 +7,6 @@ import (
"io"
"log"
"net"
- "regexp"
"strings"
"sync"
"time"
@@ -60,8 +59,6 @@ type request struct {
type requestError string
-var wordsRe = regexp.MustCompile("[[:^space:]]+")
-
var errNotImplemented = errors.New("not implemented")
func (c *client) String() string {
@@ -208,9 +205,7 @@ func (s *Server) newClient(conn net.Conn) *client {
return c
}
-func (s *Server) Command(query string) error {
- args := wordsRe.FindAllString(query, -1)
-
+func (s *Server) Command(args []string) error {
r := &request{c: &client{s: s}}
r.run(args)
@@ -290,26 +285,8 @@ func (c *client) decode(b []byte) ([]string, error) {
return t, nil
}
-func (r *request) eval(args []string) []string {
- var out []string
-
- for _, s := range args {
- var t string
-
- if strings.HasPrefix(s, ":") {
- t = s[1:]
- } else {
- t = r.c.s.env.Eval(s)
- }
-
- out = append(out, t)
- }
-
- return out
-}
-
func (r *request) parse(args []string) {
- if c, args := getCmd(r.eval(args)); c == nil {
+ if c, args := getCmd(r.c.s.env.EvalStrings(args)); c == nil {
r.Fatal("command not found")
} else {
r.args = args
diff --git a/pkg/server/socket/dial.go b/pkg/server/socket/dial.go
index 728269e..18dee8f 100644
--- a/pkg/server/socket/dial.go
+++ b/pkg/server/socket/dial.go
@@ -21,7 +21,7 @@ func (s *dialSocket) String() string {
}
func (s *dialSocket) Open(e env.Env) (Conn, error) {
- addr := e.Eval(s.addr)
+ addr := e.Expand(s.addr)
switch s.proto {
case "tcp", "udp":
diff --git a/pkg/server/socket/proxy.go b/pkg/server/socket/proxy.go
index 47f86c9..67c5769 100644
--- a/pkg/server/socket/proxy.go
+++ b/pkg/server/socket/proxy.go
@@ -35,7 +35,7 @@ func newProxySocket(proto string) (S, error) {
func (sock *proxySocket) Open(env env.Env) (Conn, error) {
s := &proxyServer{
sock: sock,
- auth: env.GetLocal("proxy.auth"),
+ auth: env.Value("proxy.auth"),
wait: make(chan status),
env: env,
}
diff --git a/test/env.sh b/test/env.sh
index 5003757..080c2b5 100755
--- a/test/env.sh
+++ b/test/env.sh
@@ -3,9 +3,8 @@
ROOT=$(dirname $0)/..
PATH=$ROOT/cmd/tunnel
-tunnel set cmd echo
-tunnel set args :"@x, @y!"
+tunnel set args "@[x], @[y]!"
tunnel set x Hello
tunnel set y World
-tunnel @cmd @args
+tunnel echo -e @args
tunnel clear