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