diff --git a/client.go b/client.go index b934735..5f98150 100644 --- a/client.go +++ b/client.go @@ -111,7 +111,7 @@ func cliHandle(conn net.Conn) { } defer c.Close() - if Websocket { + if UseWebsocket { url := &url.URL{ Host: Saddr, } @@ -123,6 +123,14 @@ func cliHandle(conn net.Conn) { resp.Body.Close() c = NewWSConn(ws) + } else if UseHttp { + httpcli := NewHttpClientConn(c) + if err := httpcli.Handshake(); err != nil { + log.Println(err) + return + } + c = httpcli + defer httpcli.Close() } sc := gosocks5.ClientConn(c, clientConfig) diff --git a/http.go b/http.go new file mode 100644 index 0000000..ca74ed1 --- /dev/null +++ b/http.go @@ -0,0 +1,230 @@ +package main + +import ( + "bufio" + "bytes" + "code.google.com/p/go-uuid/uuid" + "github.com/ginuerzh/gosocks5" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "net/url" + "time" +) + +const ( + s2cUri = "/s2c" + c2sUri = "/c2s" +) + +type HttpClientConn struct { + c net.Conn + url *url.URL + r io.ReadCloser +} + +func NewHttpClientConn(conn net.Conn) *HttpClientConn { + return &HttpClientConn{ + c: conn, + } +} + +func (conn *HttpClientConn) Handshake() error { + log.Println("remote", conn.c.RemoteAddr().String()) + req := &http.Request{ + Method: "Get", + Host: conn.c.RemoteAddr().String(), + URL: &url.URL{ + Host: "ignored", + Scheme: "http", + Path: s2cUri, + }, + } + if err := req.Write(conn.c); err != nil { + return err + } + + resp, err := http.ReadResponse(bufio.NewReader(conn.c), req) + if err != nil { + return err + } + + b := make([]byte, 36) + if _, err = io.ReadFull(resp.Body, b); err != nil { + return err + } + log.Println("token", string(b)) + q := url.Values{} + q.Set("token", string(b)) + conn.url = &url.URL{ + Scheme: "http", + Host: conn.c.RemoteAddr().String(), + Path: c2sUri, + RawQuery: q.Encode(), + } + conn.r = resp.Body + + return nil +} + +func (conn *HttpClientConn) Read(b []byte) (n int, err error) { + return conn.r.Read(b) +} + +func (conn *HttpClientConn) Write(b []byte) (n int, err error) { + c, err := Connect(Saddr, Proxy) + if err != nil { + log.Println(err) + return + } + + request, err := http.NewRequest("POST", conn.url.String(), bytes.NewReader(b)) + if err != nil { + log.Println(err) + return + } + + err = request.Write(c) + if err != nil { + log.Println(err) + return + } + + return len(b), nil +} + +func (conn *HttpClientConn) Close() error { + return conn.r.Close() +} + +func (conn *HttpClientConn) LocalAddr() net.Addr { + return conn.c.LocalAddr() +} + +func (conn *HttpClientConn) RemoteAddr() net.Addr { + return conn.c.RemoteAddr() +} + +func (conn *HttpClientConn) SetDeadline(t time.Time) error { + return conn.c.SetDeadline(t) +} + +func (conn *HttpClientConn) SetReadDeadline(t time.Time) error { + return conn.c.SetReadDeadline(t) +} + +func (conn *HttpClientConn) SetWriteDeadline(t time.Time) error { + return conn.c.SetWriteDeadline(t) +} + +type HttpServerConn struct { + w http.ResponseWriter + c chan []byte + rb []byte +} + +func NewHttpServerConn(w http.ResponseWriter, c chan []byte) *HttpServerConn { + return &HttpServerConn{ + w: w, + c: c, + } +} + +func (conn *HttpServerConn) Read(b []byte) (n int, err error) { + if len(conn.rb) == 0 { + var ok bool + if conn.rb, ok = <-conn.c; !ok { + return 0, io.EOF + } + } + n = copy(b, conn.rb) + conn.rb = conn.rb[n:] + + //log.Println("ws r:", n) + + return +} + +func (conn *HttpServerConn) Write(b []byte) (n int, err error) { + n, err = conn.w.Write(b) + if f, ok := conn.w.(http.Flusher); ok { + f.Flush() + } + return +} + +func (conn *HttpServerConn) Close() error { + return nil +} + +func (conn *HttpServerConn) LocalAddr() net.Addr { + return nil +} + +func (conn *HttpServerConn) RemoteAddr() net.Addr { + return nil +} + +func (conn *HttpServerConn) SetDeadline(t time.Time) error { + return nil +} + +func (conn *HttpServerConn) SetReadDeadline(t time.Time) error { + return nil +} + +func (conn *HttpServerConn) SetWriteDeadline(t time.Time) error { + return nil +} + +type HttpServer struct { + Addr string + chans map[string]chan []byte +} + +func (s *HttpServer) s2c(w http.ResponseWriter, r *http.Request) { + token := uuid.New() + ch := make(chan []byte, 1) + + conn := NewHttpServerConn(w, ch) + if _, err := conn.Write([]byte(token)); err != nil { + return + } + + s.chans[token] = ch + defer delete(s.chans, token) + + c := gosocks5.ServerConn(conn, serverConfig) + socks5Handle(c) +} + +func (s *HttpServer) c2s(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + token := r.FormValue("token") + ch := s.chans[token] + if ch == nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + b, err := ioutil.ReadAll(r.Body) + if err != nil || len(b) == 0 { + close(ch) + delete(s.chans, token) + return + } + ch <- b +} + +func (s *HttpServer) ListenAndServe() error { + s.chans = make(map[string]chan []byte) + http.HandleFunc(s2cUri, s.s2c) + http.HandleFunc(c2sUri, s.c2s) + return http.ListenAndServe(s.Addr, nil) +} diff --git a/main.go b/main.go index 43f3e71..57bc45a 100644 --- a/main.go +++ b/main.go @@ -9,13 +9,13 @@ import ( ) var ( - Laddr, Saddr, Proxy string - Websocket bool - Shadows bool - SMethod, SPassword string - Method, Password string - CertFile, KeyFile string - PrintVersion bool + Laddr, Saddr, Proxy string + UseWebsocket, UseHttp bool + Shadows bool + SMethod, SPassword string + Method, Password string + CertFile, KeyFile string + PrintVersion bool ) func init() { @@ -27,7 +27,8 @@ func init() { flag.StringVar(&CertFile, "cert", "", "cert file for tls") flag.StringVar(&KeyFile, "key", "", "key file for tls") flag.BoolVar(&Shadows, "ss", false, "run as shadowsocks server") - flag.BoolVar(&Websocket, "ws", false, "use websocket for tunnel") + flag.BoolVar(&UseWebsocket, "ws", false, "use websocket for tunnel") + flag.BoolVar(&UseHttp, "http", false, "use http for tunnel") flag.StringVar(&SMethod, "sm", "rc4-md5", "shadowsocks cipher method") flag.StringVar(&SPassword, "sp", "ginuerzh@gmail.com", "shadowsocks cipher password") flag.BoolVar(&PrintVersion, "v", false, "print version") @@ -50,8 +51,10 @@ func main() { if len(Saddr) == 0 { var server Server - if Websocket { + if UseWebsocket { server = &WSServer{Addr: Laddr} + } else if UseHttp { + server = &HttpServer{Addr: Laddr} } else { server = &Socks5Server{Addr: Laddr} } diff --git a/version.go b/version.go index e19ed97..ab60f86 100644 --- a/version.go +++ b/version.go @@ -5,7 +5,7 @@ import ( ) const ( - Version = "1.3" + Version = "1.4" ) func printVersion() {