From d91bc0bcf6204ae1228c548529044bd568cfc05f Mon Sep 17 00:00:00 2001 From: fangdingjun Date: Wed, 9 May 2018 17:16:07 +0800 Subject: [PATCH] add dynamic http forward throungh secure channel like dynamic forward, but this accept HTTP request incoming, not socks5. The destination is determined by http request, the quest is forwarded through ssh secure channel. --- client.go | 118 ++++++++++++++++++++++++++++++++++++- conn_test.go | 6 +- obfssh/config.go | 4 +- obfssh/config_example.yaml | 13 ++++ obfssh/proxy.go | 5 +- obfssh/ssh.go | 22 +++++-- server.go | 5 +- 7 files changed, 158 insertions(+), 15 deletions(-) diff --git a/client.go b/client.go index 4e31066..983ea19 100644 --- a/client.go +++ b/client.go @@ -1,15 +1,20 @@ package obfssh import ( + "bufio" "fmt" - socks "github.com/fangdingjun/socks-go" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/terminal" + "io" "net" + "net/http" "os" "os/signal" + "strings" "syscall" "time" + + socks "github.com/fangdingjun/socks-go" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/terminal" ) // Client is ssh client connection @@ -333,3 +338,110 @@ func (cc *Client) registerSignal() { } } } + +// AddDynamicHTTPForward add a http dynamic forward through +// secure channel +func (cc *Client) AddDynamicHTTPForward(addr string) error { + l, err := net.Listen("tcp", addr) + if err != nil { + Log(ERROR, "listen on %s failed, %s", addr, err) + return err + } + + cc.listeners = append(cc.listeners, l) + + go func(l net.Listener) { + // defer l.Close() + for { + c, err := l.Accept() + if err != nil { + Log(ERROR, "accept error %s", err) + break + } + go cc.handleHTTPIncoming(c) + } + }(l) + return nil +} + +func (cc *Client) handleHTTPIncoming(c net.Conn) { + defer c.Close() + + r := bufio.NewReader(c) + + req, err := http.ReadRequest(r) + if err != nil { + Log(ERROR, "read http request error %s", err) + return + } + + if req.Method == "CONNECT" { + cc.handleConnect(req, c) + return + } + cc.handleHTTPReq(req, c) +} + +func (cc *Client) handleConnect(req *http.Request, c net.Conn) { + Log(DEBUG, "connect to %s", req.RequestURI) + + c1, err := cc.client.Dial("tcp", req.RequestURI) + if err != nil { + fmt.Fprintf(c, "HTTP/1.0 503 connection failed\r\n\r\n") + Log(ERROR, "dial error %s", err) + return + } + + defer c1.Close() + + fmt.Fprintf(c, "HTTP/1.0 200 connection established\r\n\r\n") + + ch := make(chan struct{}, 2) + go func() { + io.Copy(c1, c) + ch <- struct{}{} + }() + + go func() { + io.Copy(c, c1) + ch <- struct{}{} + }() + + <-ch +} + +func (cc *Client) handleHTTPReq(req *http.Request, c net.Conn) { + host := req.Host + if !strings.Contains(host, ":") { + host = fmt.Sprintf("%s:80", host) + } + + Log(DEBUG, "request to %s", host) + c1, err := cc.client.Dial("tcp", host) + if err != nil { + fmt.Fprintf(c, "HTTP/1.1 503 connection failed\r\nConnection: close\r\n\r\n") + Log(ERROR, "connection failed %s", err) + return + } + defer c1.Close() + + if err = req.Write(c1); err != nil { + fmt.Fprintf(c, "HTTP/1.1 503 write to server error\r\nConnection: close\r\n\r\n") + Log(ERROR, "write request to server error %s", err) + return + } + + ch := make(chan struct{}, 2) + + go func() { + io.Copy(c1, c) + ch <- struct{}{} + }() + + go func() { + io.Copy(c, c1) + ch <- struct{}{} + }() + + <-ch +} diff --git a/conn_test.go b/conn_test.go index bac3640..03f7beb 100644 --- a/conn_test.go +++ b/conn_test.go @@ -7,7 +7,7 @@ import ( "time" ) -func testTimedOutConn(t *testing.T, timeout bool) { +func testTimedOutConn(t *testing.T, _timeout bool) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen failed: %s", err) @@ -37,7 +37,7 @@ func testTimedOutConn(t *testing.T, timeout bool) { t.Fatalf("server read failed: %s", err) } - if timeout { + if _timeout { time.Sleep(timeout + 1*time.Second) } @@ -62,7 +62,7 @@ func testTimedOutConn(t *testing.T, timeout bool) { buf := make([]byte, 100) n, err := cConn.Read(buf) - if timeout { + if _timeout { if err == nil { t.Errorf("expeced timeout error, got nil") } else { diff --git a/obfssh/config.go b/obfssh/config.go index 2d4afa6..6290c76 100644 --- a/obfssh/config.go +++ b/obfssh/config.go @@ -2,9 +2,10 @@ package main import ( "flag" - "github.com/go-yaml/yaml" "io/ioutil" "strings" + + "github.com/go-yaml/yaml" ) // stringSlice implemnts the flag.Value interface @@ -58,6 +59,7 @@ type config struct { LocalForwards stringSlice `yaml:"local_forward"` RemoteForwards stringSlice `yaml:"remote_forward"` DynamicForwards stringSlice `yaml:"dynamic_forward"` + DynamicHTTP stringSlice `yaml:"dynamic_http"` Proxy proxy } diff --git a/obfssh/config_example.yaml b/obfssh/config_example.yaml index 200e034..b00b2d1 100644 --- a/obfssh/config_example.yaml +++ b/obfssh/config_example.yaml @@ -154,3 +154,16 @@ # dynamic_forward: # - :3224 # - 127.0.0.1:9883 + + +# dynamic_http +# +# Listen a port to accept http request incoming +# and dynamic forward the request +# to remote server through secure channel. +# This option can be specified multiple times. +# format [bind_adress:]port + +# dynamic_http: +# - :8080 +# - :127.0.0.1:8180 diff --git a/obfssh/proxy.go b/obfssh/proxy.go index 6c5a803..6d43710 100644 --- a/obfssh/proxy.go +++ b/obfssh/proxy.go @@ -4,8 +4,6 @@ import ( "bufio" "crypto/tls" "fmt" - "github.com/fangdingjun/obfssh" - socks "github.com/fangdingjun/socks-go" "io" "net" "net/textproto" @@ -14,6 +12,9 @@ import ( "strconv" "strings" "time" + + "github.com/fangdingjun/obfssh" + socks "github.com/fangdingjun/socks-go" ) type httpProxyConn struct { diff --git a/obfssh/ssh.go b/obfssh/ssh.go index dfcc598..27fc009 100644 --- a/obfssh/ssh.go +++ b/obfssh/ssh.go @@ -4,10 +4,6 @@ import ( "crypto/tls" "flag" "fmt" - "github.com/bgentry/speakeasy" - "github.com/fangdingjun/obfssh" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/agent" "io/ioutil" "log" "net" @@ -15,6 +11,11 @@ import ( "path/filepath" "strings" "time" + + "github.com/bgentry/speakeasy" + "github.com/fangdingjun/obfssh" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) var dialer = &net.Dialer{Timeout: 15 * time.Second} @@ -270,6 +271,19 @@ func main() { } } + for _, p := range cfg.DynamicHTTP { + if strings.Index(p, ":") == -1 { + local = fmt.Sprintf(":%s", p) + } else { + local = p + } + //log.Printf("listen on %s", local) + if err := client.AddDynamicHTTPForward(local); err != nil { + log.Println(err) + } + + } + hasErr := false if !cfg.NotRunCmd { diff --git a/server.go b/server.go index 41492bc..19b758d 100644 --- a/server.go +++ b/server.go @@ -2,10 +2,11 @@ package obfssh import ( "fmt" - "github.com/pkg/sftp" - "golang.org/x/crypto/ssh" "net" "time" + + "github.com/pkg/sftp" + "golang.org/x/crypto/ssh" ) // Server is server connection