summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--pkg/http/http.go166
-rw-r--r--pkg/server/env/env.go8
-rw-r--r--pkg/server/hook/aes.go2
-rw-r--r--pkg/server/hook/auth.go2
-rw-r--r--pkg/server/hook/hook.go8
-rw-r--r--pkg/server/hook/look-http.go9
-rw-r--r--pkg/server/hook/proxy.go89
-rw-r--r--pkg/server/hook/tee.go2
-rw-r--r--pkg/server/socket/defer.go49
-rw-r--r--pkg/server/socket/dial.go2
-rw-r--r--pkg/server/socket/listen.go2
-rw-r--r--pkg/server/socket/loop.go14
-rw-r--r--pkg/server/socket/proxy.go134
-rw-r--r--pkg/server/socket/socket.go19
-rw-r--r--pkg/server/socket/tun.go14
-rw-r--r--pkg/server/tunnel.go11
-rwxr-xr-xtest/auth.sh1
-rwxr-xr-xtest/proxy.sh15
19 files changed, 406 insertions, 144 deletions
diff --git a/TODO b/TODO
index 4c7ebf5..739111e 100644
--- a/TODO
+++ b/TODO
@@ -2,7 +2,8 @@
- tunnel enable/disable
- stream.run check errors on Open
- per tunnel, per stream statistics
-- http connect server proxy module
- systemd socket activation
- tun: separate data and control packets (route)
- tun: auto created interface and auto ip address from pool
+- go tests
+- docs
diff --git a/pkg/http/http.go b/pkg/http/http.go
new file mode 100644
index 0000000..eefe348
--- /dev/null
+++ b/pkg/http/http.go
@@ -0,0 +1,166 @@
+package http
+
+import (
+ "bufio"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "io"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+type Header map[string]string
+
+type Request struct {
+ Method string
+ URI string
+ Proto string
+ Header Header
+}
+
+type Response struct {
+ Code int
+ Desc string
+ Proto string
+ Header Header
+}
+
+var version = "HTTP/[0-9]+\\.[0-9]+"
+var requestLine = regexp.MustCompile("^([A-Z]+) +([^ ]+) +(" + version + ")$")
+var statusLine = regexp.MustCompile("^(" + version + ") +([0-9]+) +(.*)$")
+var header = regexp.MustCompile("^([^ ]+) *: *(.*)$")
+
+const OK = 200
+
+func parseHeader(s string) (key, value string, ok bool) {
+ if m := header.FindStringSubmatch(s); m == nil {
+ return
+ } else {
+ return m[1], m[2], true
+ }
+}
+
+func addHeader(m Header, k, v string) Header {
+ if m == nil {
+ m = make(Header)
+ }
+
+ m[strings.Title(k)] = v
+
+ return m
+}
+
+func parse(r io.Reader, init func(string) error, f func(string) error) error {
+ scanner := bufio.NewScanner(r)
+ ok := false
+
+ if scanner.Scan() {
+ if err := init(scanner.Text()); err != nil {
+ return err
+ }
+ }
+
+ for scanner.Scan() {
+ if scanner.Text() == "" {
+ ok = true
+ break
+ }
+
+ if err := f(scanner.Text()); err != nil {
+ return err
+ }
+ }
+
+ if err := scanner.Err(); err != nil {
+ return err
+ } else if !ok {
+ return io.ErrUnexpectedEOF
+ }
+
+ return nil
+}
+
+func ParseRequestLine(s string) (method, uri, proto string, ok bool) {
+ if m := requestLine.FindStringSubmatch(s); m == nil {
+ return
+ } else {
+ return m[1], m[2], m[3], true
+ }
+}
+
+func (r *Request) parseRequestLine(s string) error {
+ if method, uri, proto, ok := ParseRequestLine(s); !ok {
+ return errors.New("request line parse failed")
+ } else {
+ r.Method, r.URI, r.Proto = method, uri, proto
+ return nil
+ }
+}
+
+func (r *Request) parseHeader(s string) error {
+ if k, v, ok := parseHeader(s); !ok {
+ return errors.New("header parse failed")
+ } else {
+ r.Header = addHeader(r.Header, k, v)
+ return nil
+ }
+}
+
+func ParseRequest(r io.Reader) (*Request, error) {
+ req := new(Request)
+
+ if err := parse(r, req.parseRequestLine, req.parseHeader); err != nil {
+ return nil, fmt.Errorf("http: bad request: %w", err)
+ }
+
+ return req, nil
+}
+
+func ParseStatusLine(s string) (proto string, code int, desc string, ok bool) {
+ if m := statusLine.FindStringSubmatch(s); m == nil {
+ return
+ } else if n, err := strconv.Atoi(m[2]); err != nil {
+ return
+ } else {
+ return m[1], n, m[3], true
+ }
+}
+
+func (r *Response) parseStatusLine(s string) error {
+ if proto, code, desc, ok := ParseStatusLine(s); !ok {
+ return errors.New("status line parse failed")
+ } else {
+ r.Code, r.Desc, r.Proto = code, desc, proto
+ return nil
+ }
+}
+
+func (r *Response) parseHeader(s string) error {
+ if k, v, ok := parseHeader(s); !ok {
+ return errors.New("header parse failed")
+ } else {
+ r.Header = addHeader(r.Header, k, v)
+ return nil
+ }
+}
+
+func ParseResponse(r io.Reader) (*Response, error) {
+ resp := new(Response)
+
+ if err := parse(r, resp.parseStatusLine, resp.parseHeader); err != nil {
+ return nil, fmt.Errorf("http: bad response: %w", err)
+ }
+
+ return resp, nil
+}
+
+func BasicAuthEncode(s string) string {
+ encoded := base64.StdEncoding.EncodeToString([]byte(s))
+ return "Basic " + encoded
+}
+
+func BasicAuthCheck(origin string, encoded string) bool {
+ return BasicAuthEncode(origin) == encoded
+}
diff --git a/pkg/server/env/env.go b/pkg/server/env/env.go
index 5a49532..45650ab 100644
--- a/pkg/server/env/env.go
+++ b/pkg/server/env/env.go
@@ -148,3 +148,11 @@ 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
+ }
+
+ return e.Get(key)
+}
diff --git a/pkg/server/hook/aes.go b/pkg/server/hook/aes.go
index dc9605a..8ae47a3 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 := getHookVar(env, "secret")
+ s := env.GetLocal("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 b31855a..14ad114 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: getHookVar(env, "secret"),
+ secret: env.GetLocal("secret"),
recvChallenge: make(chan struct{}),
recvHash: make(chan struct{}),
fail: make(chan struct{}),
diff --git a/pkg/server/hook/hook.go b/pkg/server/hook/hook.go
index 1702afd..6ac51a1 100644
--- a/pkg/server/hook/hook.go
+++ b/pkg/server/hook/hook.go
@@ -113,14 +113,6 @@ func New(desc string, env env.Env) (H, error) {
}
}
-func getHookVar(env env.Env, s string) string {
- if v := env.Eval("@{tunnel.@{tunnel}." + s + "}"); v != "" {
- return v
- }
-
- return env.Get(s)
-}
-
func register(name string, f hookInitFunc) {
if _, ok := hooks[name]; ok {
log.Panicf("duplicate hook name '%s'", name)
diff --git a/pkg/server/hook/look-http.go b/pkg/server/hook/look-http.go
index d467563..50ec663 100644
--- a/pkg/server/hook/look-http.go
+++ b/pkg/server/hook/look-http.go
@@ -2,7 +2,7 @@ package hook
import (
"bufio"
- "strings"
+ "tunnel/pkg/http"
"tunnel/pkg/server/env"
"tunnel/pkg/server/opts"
"tunnel/pkg/server/queue"
@@ -28,11 +28,8 @@ func (look *lookHttp) Send(rq, wq queue.Q) error {
}()
if s.Scan() {
- if w := strings.Split(s.Text(), " "); len(w) > 1 {
- switch m := strings.ToUpper(w[0]); m {
- case "CONNECT", "GET", "POST":
- look.env.Set("look.info", m+" "+w[1])
- }
+ if method, uri, _, ok := http.ParseRequestLine(s.Text()); ok {
+ look.env.Set("look.info", method+" "+uri)
}
}
diff --git a/pkg/server/hook/proxy.go b/pkg/server/hook/proxy.go
index 64db784..26be2d0 100644
--- a/pkg/server/hook/proxy.go
+++ b/pkg/server/hook/proxy.go
@@ -1,23 +1,17 @@
package hook
import (
- "bufio"
"bytes"
- "encoding/base64"
"errors"
"fmt"
- "io"
"regexp"
- "strconv"
+ "tunnel/pkg/http"
"tunnel/pkg/server/env"
"tunnel/pkg/server/opts"
"tunnel/pkg/server/queue"
)
var addrRe = regexp.MustCompile("^[0-9a-zA-Z-.]+:[0-9]+$")
-var respRe = regexp.MustCompile("^([^ ]+) +([0-9]+) +(.*)$")
-
-var errBadHttpResponse = errors.New("bad HTTP response")
type proxyHook struct {
addr string
@@ -27,93 +21,41 @@ type proxyHook struct {
type proxy struct {
addr string
auth string
- ok chan struct{}
- fail chan struct{}
+ c chan bool
}
func (p *proxy) Send(rq, wq queue.Q) error {
var out bytes.Buffer
- fmt.Fprintf(&out, "CONNECT %s HTTP/1.0\r\n", p.addr)
+ fmt.Fprintf(&out, "CONNECT %s HTTP/1.1\r\n", p.addr)
if p.auth != "" {
- encoded := base64.StdEncoding.EncodeToString([]byte(p.auth))
- fmt.Fprintf(&out, "Proxy-Authorization: Basic %s\r\n", encoded)
+ encoded := http.BasicAuthEncode(p.auth)
+ fmt.Fprintf(&out, "Proxy-Authorization: %s\r\n", encoded)
}
fmt.Fprintf(&out, "\r\n")
wq <- out.Bytes()
- select {
- case <-p.fail:
+ if !<-p.c {
return nil
- case <-p.ok:
}
return queue.Copy(rq, wq)
}
-func parseProxyResponse(s string) error {
- var version string
- var code int
- var desc string
-
- if m := respRe.FindStringSubmatch(s); m == nil {
- return errBadHttpResponse
- } else {
- version = m[1]
- if c, err := strconv.Atoi(m[2]); err != nil {
- return errBadHttpResponse
- } else {
- code = c
- }
- desc = m[3]
- }
-
- if version != "HTTP/1.0" && version != "HTTP/1.1" {
- return errBadHttpResponse
- }
-
- if code != 200 {
- return fmt.Errorf("connect failed: %d %s", code, desc)
- }
-
- return nil
-}
-
-func (p *proxy) Recv(rq, wq queue.Q) (err error) {
- defer func() {
- if err != nil {
- close(p.fail)
- }
- }()
-
- s := bufio.NewScanner(rq.Reader())
-
- var resp bool
-
- for s.Scan() {
- line := s.Text()
-
- if !resp {
- if err := parseProxyResponse(line); err != nil {
- return err
- }
- resp = true
- continue
- }
+func (p *proxy) Recv(rq, wq queue.Q) error {
+ resp, err := http.ParseResponse(rq.Reader())
- if line == "" {
- break
- }
+ if err == nil && resp.Code != http.OK {
+ err = fmt.Errorf("connect failed: %d %s", resp.Code, resp.Desc)
}
- if err := s.Err(); err != nil {
+ if err != nil {
+ p.c <- false
return err
- } else if !resp {
- return io.ErrUnexpectedEOF
}
- close(p.ok)
+ p.c <- true
return queue.Copy(rq, wq)
}
@@ -122,12 +64,11 @@ func (h *proxyHook) Open(env env.Env) (interface{}, error) {
p := &proxy{
addr: h.addr,
auth: h.auth,
- ok: make(chan struct{}),
- fail: make(chan struct{}),
+ c: make(chan bool),
}
if p.auth == "" {
- p.auth = getHookVar(env, "proxy.auth")
+ p.auth = env.GetLocal("proxy.auth")
}
return p, nil
diff --git a/pkg/server/hook/tee.go b/pkg/server/hook/tee.go
index 521164b..2d13fcb 100644
--- a/pkg/server/hook/tee.go
+++ b/pkg/server/hook/tee.go
@@ -64,7 +64,7 @@ func (h *teeHook) where(env env.Env) string {
return h.file
}
- if v := getHookVar(env, "tee.file"); v != "" {
+ if v := env.GetLocal("tee.file"); v != "" {
return v
}
diff --git a/pkg/server/socket/defer.go b/pkg/server/socket/defer.go
index bfb2157..7ed303d 100644
--- a/pkg/server/socket/defer.go
+++ b/pkg/server/socket/defer.go
@@ -9,10 +9,11 @@ type deferSocket struct {
S
}
-type deferChannel struct {
- s *deferSocket
- c chan Channel
- e env.Env
+type deferConn struct {
+ sock *deferSocket
+ wait chan bool
+ env env.Env
+ conn Conn
}
func newDeferSocket(proto, addr string) (S, error) {
@@ -24,42 +25,43 @@ func newDeferSocket(proto, addr string) (S, error) {
return &deferSocket{s}, nil
}
-func (s *deferSocket) Open(env env.Env) (Channel, error) {
- c := &deferChannel{
- s: s,
- c: make(chan Channel),
- e: env,
+func (s *deferSocket) Open(env env.Env) (Conn, error) {
+ c := &deferConn{
+ sock: s,
+ wait: make(chan bool),
+ env: env,
}
return c, nil
}
-func (c *deferChannel) String() string {
+func (c *deferConn) String() string {
return "defer"
}
-func (c *deferChannel) Send(wq queue.Q) error {
- if x := <-c.c; x == nil {
+func (c *deferConn) Send(wq queue.Q) error {
+ if !<-c.wait {
return nil
} else {
- return x.Send(wq)
+ return c.conn.Send(wq)
}
}
-func (c *deferChannel) Recv(rq queue.Q) error {
+func (c *deferConn) Recv(rq queue.Q) error {
b := <-rq
if b == nil {
- close(c.c)
+ c.wait <- false
return nil
}
- x, err := c.s.S.Open(c.e)
+ conn, err := c.sock.S.Open(c.env)
if err != nil {
- close(c.c)
+ c.wait <- false
return err
}
- c.c <- x
+ c.conn = conn
+ c.wait <- true
q := queue.New()
@@ -71,10 +73,13 @@ func (c *deferChannel) Recv(rq queue.Q) error {
defer q.Dry()
- return x.Recv(q)
+ return c.conn.Recv(q)
}
-/* TODO */
-func (c *deferChannel) Close() error {
- return nil
+func (c *deferConn) Close() (err error) {
+ if c.conn != nil {
+ err = c.conn.Close()
+ }
+
+ return
}
diff --git a/pkg/server/socket/dial.go b/pkg/server/socket/dial.go
index d4c20e1..d7b232c 100644
--- a/pkg/server/socket/dial.go
+++ b/pkg/server/socket/dial.go
@@ -26,7 +26,7 @@ func (s *dialSocket) String() string {
return fmt.Sprintf("%s/%s", s.proto, s.addr)
}
-func (s *dialSocket) Open(e env.Env) (Channel, error) {
+func (s *dialSocket) Open(e env.Env) (Conn, error) {
conn, err := net.Dial(s.proto, e.Eval(s.addr))
if err != nil {
return nil, err
diff --git a/pkg/server/socket/listen.go b/pkg/server/socket/listen.go
index c328945..caf5fcf 100644
--- a/pkg/server/socket/listen.go
+++ b/pkg/server/socket/listen.go
@@ -28,7 +28,7 @@ func newListenSocket(proto, addr string) (S, error) {
return s, nil
}
-func (s *listenSocket) Open(env.Env) (Channel, error) {
+func (s *listenSocket) Open(env.Env) (Conn, error) {
conn, err := s.listen.Accept()
if err != nil {
return nil, err
diff --git a/pkg/server/socket/loop.go b/pkg/server/socket/loop.go
index 88e9491..a06448a 100644
--- a/pkg/server/socket/loop.go
+++ b/pkg/server/socket/loop.go
@@ -7,31 +7,31 @@ import (
type loopSocket struct{}
-type loopChannel struct {
+type loopConn struct {
c chan queue.Q
q chan error
}
-func (c *loopChannel) Send(wq queue.Q) error {
+func (c *loopConn) Send(wq queue.Q) error {
c.c <- wq
return <-c.q
}
-func (c *loopChannel) Recv(rq queue.Q) error {
+func (c *loopConn) Recv(rq queue.Q) error {
defer close(c.q)
return queue.Copy(rq, <-c.c)
}
-func (c *loopChannel) String() string {
+func (c *loopConn) String() string {
return "loop"
}
-func (c *loopChannel) Close() error {
+func (c *loopConn) Close() error {
return nil
}
-func (s *loopSocket) Open(env.Env) (Channel, error) {
- return &loopChannel{make(chan queue.Q), make(chan error)}, nil
+func (s *loopSocket) Open(env.Env) (Conn, error) {
+ return &loopConn{make(chan queue.Q), make(chan error)}, nil
}
func (s *loopSocket) String() string {
diff --git a/pkg/server/socket/proxy.go b/pkg/server/socket/proxy.go
new file mode 100644
index 0000000..ef14f48
--- /dev/null
+++ b/pkg/server/socket/proxy.go
@@ -0,0 +1,134 @@
+package socket
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "tunnel/pkg/http"
+ "tunnel/pkg/server/env"
+ "tunnel/pkg/server/queue"
+)
+
+type status struct {
+ code int
+ desc string
+}
+
+type proxySocket struct {
+ proto string
+}
+
+type proxyServer struct {
+ sock *proxySocket
+ addr string
+ auth string
+ wait chan status
+ env env.Env
+ conn Conn
+}
+
+func newProxySocket(proto string) (S, error) {
+ return &proxySocket{proto}, nil
+}
+
+func (sock *proxySocket) Open(env env.Env) (Conn, error) {
+ s := &proxyServer{
+ sock: sock,
+ auth: env.GetLocal("proxy.auth"),
+ wait: make(chan status),
+ env: env,
+ }
+
+ return s, nil
+}
+
+func (sock *proxySocket) Close() {
+}
+
+func (s *proxyServer) String() string {
+ return "proxy"
+}
+
+func (s *proxyServer) Send(wq queue.Q) error {
+ var out bytes.Buffer
+
+ status := <-s.wait
+
+ fmt.Fprintf(&out, "HTTP/1.0 %d %s\r\n", status.code, status.desc)
+ switch status.code {
+ case http.OK:
+ fmt.Fprintf(&out, "Proxy-Agent: tunnel\r\n")
+ case 407:
+ fmt.Fprintf(&out, "Proxy-Authenticate: Basic realm=\"tunnel\"\r\n")
+ fallthrough
+ default:
+ fmt.Fprintf(&out, "Server: tunnel\r\n")
+ fmt.Fprintf(&out, "Connection: close\r\n")
+ }
+ fmt.Fprintf(&out, "\r\n")
+
+ wq <- out.Bytes()
+
+ if status.code != http.OK {
+ return nil
+ }
+
+ return s.conn.Send(wq)
+}
+
+func (s *proxyServer) initConn(addr string) error {
+ dial, err := newDialSocket(s.sock.proto, addr)
+ if err != nil {
+ return err
+ }
+
+ conn, err := dial.Open(s.env)
+ if err != nil {
+ dial.Close()
+ return err
+ }
+
+ s.conn = conn
+
+ return nil
+}
+
+func (s *proxyServer) Recv(rq queue.Q) error {
+ req, err := http.ParseRequest(rq.Reader())
+ if err != nil {
+ s.wait <- status{400, "Bad Request"}
+ return err
+ }
+
+ if req.Method != "CONNECT" {
+ s.wait <- status{400, "Bad Request"}
+ return errors.New("bad method")
+ }
+
+ if s.auth != "" {
+ if auth, ok := req.Header["Proxy-Authorization"]; !ok {
+ s.wait <- status{407, "Proxy Authentication Required"}
+ return errors.New("auth required")
+ } else if !http.BasicAuthCheck(s.auth, auth) {
+ s.wait <- status{401, "Unauthorized"}
+ return errors.New("auth failed")
+ }
+ }
+
+ if err := s.initConn(req.URI); err != nil {
+ s.wait <- status{500, "Unable to connect"}
+ return err
+ }
+
+ s.wait <- status{200, "Connection established"}
+
+ return s.conn.Recv(rq)
+}
+
+func (s *proxyServer) Close() (err error) {
+ if s.conn != nil {
+ err = s.conn.Close()
+ }
+
+ return
+}
diff --git a/pkg/server/socket/socket.go b/pkg/server/socket/socket.go
index 1d447f8..48c06b4 100644
--- a/pkg/server/socket/socket.go
+++ b/pkg/server/socket/socket.go
@@ -16,17 +16,17 @@ var ErrAlreadyClosed = errors.New("already closed")
type exported struct {
info string
- Channel
+ Conn
}
-type Channel interface {
+type Conn interface {
Send(wq queue.Q) error
Recv(rq queue.Q) error
Close() error
}
type S interface {
- Open(env env.Env) (Channel, error)
+ Open(env env.Env) (Conn, error)
Close()
}
@@ -44,7 +44,7 @@ func (c exported) String() string {
return c.info
}
-func newConn(cn net.Conn) Channel {
+func newConn(cn net.Conn) Conn {
c := &conn{Conn: cn}
log.Println("open", c)
return c
@@ -87,15 +87,16 @@ func New(desc string, env env.Env) (S, error) {
proto, addr = args[0], args[1]
}
- if addr == "loop" {
- return newLoopSocket()
- }
-
if proto == "" {
proto = "tcp"
}
- if addr == "" {
+ switch addr {
+ case "loop":
+ return newLoopSocket()
+ case "proxy":
+ return newProxySocket(proto)
+ case "":
return nil, fmt.Errorf("bad socket '%s'", desc)
}
diff --git a/pkg/server/socket/tun.go b/pkg/server/socket/tun.go
index 78bdfd4..c14125b 100644
--- a/pkg/server/socket/tun.go
+++ b/pkg/server/socket/tun.go
@@ -27,7 +27,7 @@ type tunSocket struct {
name string
}
-type tunChannel struct {
+type tunConn struct {
name string
s *tunSocket
fp *os.File
@@ -52,7 +52,7 @@ func (s *tunSocket) String() string {
return fmt.Sprintf("tun/%s", s.name)
}
-func (s *tunSocket) Open(env.Env) (Channel, error) {
+func (s *tunSocket) Open(env.Env) (Conn, error) {
fd, err := unix.Open("/dev/net/tun", unix.O_RDWR, 0)
if err != nil {
return nil, err
@@ -72,7 +72,7 @@ func (s *tunSocket) Open(env.Env) (Channel, error) {
return nil, fmt.Errorf("set nonblock %s: %w", s.name, err)
}
- c := &tunChannel{
+ c := &tunConn{
name: strings.Trim(string(ifr.name[:]), "\x00"),
fp: os.NewFile(uintptr(fd), "tun"),
}
@@ -83,7 +83,7 @@ func (s *tunSocket) Open(env.Env) (Channel, error) {
func (s *tunSocket) Close() {
}
-func (c *tunChannel) Send(wq queue.Q) error {
+func (c *tunConn) Send(wq queue.Q) error {
buf := make([]byte, maxTunBufSize)
enc := pack.NewEncoder(wq.Writer())
@@ -97,7 +97,7 @@ func (c *tunChannel) Send(wq queue.Q) error {
}
}
-func (c *tunChannel) Recv(rq queue.Q) error {
+func (c *tunConn) Recv(rq queue.Q) error {
dec := pack.NewDecoder(rq.Reader())
for {
@@ -117,11 +117,11 @@ func (c *tunChannel) Recv(rq queue.Q) error {
}
}
-func (c *tunChannel) String() string {
+func (c *tunConn) String() string {
return "tun/" + c.name
}
-func (c *tunChannel) Close() error {
+func (c *tunConn) Close() error {
err := ErrAlreadyClosed
c.once.Do(func() {
diff --git a/pkg/server/tunnel.go b/pkg/server/tunnel.go
index 4543714..e47215e 100644
--- a/pkg/server/tunnel.go
+++ b/pkg/server/tunnel.go
@@ -33,7 +33,7 @@ type stream struct {
since time.Time
until time.Time
wg sync.WaitGroup
- in, out socket.Channel
+ in, out socket.Conn
pipes []*hook.Pipe
m struct {
@@ -135,6 +135,8 @@ func (t *tunnel) serve() {
env := t.env.Fork()
+ env.Set("tunnel", t.id)
+
if in, err := t.in.Open(env); err == nil {
if out, err := t.out.Open(env); err == nil {
s := t.newStream(env, in, out)
@@ -157,7 +159,7 @@ func (t *tunnel) serve() {
close(t.done)
}
-func (t *tunnel) newStream(env env.Env, in, out socket.Channel) *stream {
+func (t *tunnel) newStream(env env.Env, in, out socket.Conn) *stream {
s := &stream{
t: t,
in: in,
@@ -167,7 +169,6 @@ func (t *tunnel) newStream(env env.Env, in, out socket.Channel) *stream {
since: time.Now(),
}
- s.env.Set("tunnel", t.id)
s.env.Set("stream", strconv.Itoa(s.id))
s.run()
@@ -230,7 +231,7 @@ func (s *stream) waitAndClose() {
log.Println(s.t, s, "done", s.info())
}
-func (s *stream) channel(c socket.Channel, m *metric, rq, wq queue.Q) {
+func (s *stream) channel(c socket.Conn, m *metric, rq, wq queue.Q) {
watch := func(q queue.Q, f func(q queue.Q) error) {
defer s.wg.Done()
@@ -249,7 +250,7 @@ func (s *stream) channel(c socket.Channel, m *metric, rq, wq queue.Q) {
}
if err != nil {
- log.Println(s.t, s, err)
+ log.Println(s.t, s, c, err)
}
}
diff --git a/test/auth.sh b/test/auth.sh
index 58b1180..66855e2 100755
--- a/test/auth.sh
+++ b/test/auth.sh
@@ -9,6 +9,7 @@ tunnel set tunnel.T.secret secret
tunnel set tunnel.X.secret secret
nc -l 4000 &
echo "Hello, World!" | nc -N localhost 2000
+wait
tunnel clear
tunnel del T
tunnel del X
diff --git a/test/proxy.sh b/test/proxy.sh
new file mode 100755
index 0000000..ec513fe
--- /dev/null
+++ b/test/proxy.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+ROOT=$(dirname $0)/..
+PATH=$ROOT/cmd/tunnel:$PATH
+
+tunnel add name C 2000,listen proxy,addr=localhost:4000 3000
+tunnel add name S 3000,listen proxy
+tunnel set tunnel.C.proxy.auth user:password
+tunnel set tunnel.S.proxy.auth user:password
+nc -l 4000 &
+echo "Hello, World!" | nc -N localhost 2000
+wait
+tunnel clear
+tunnel del C
+tunnel del S