tunnel over http

This commit is contained in:
rui.zheng 2015-06-04 10:57:29 +08:00
parent 0ffb1daf27
commit 7dffd77cb8
4 changed files with 252 additions and 11 deletions

View File

@ -111,7 +111,7 @@ func cliHandle(conn net.Conn) {
} }
defer c.Close() defer c.Close()
if Websocket { if UseWebsocket {
url := &url.URL{ url := &url.URL{
Host: Saddr, Host: Saddr,
} }
@ -123,6 +123,14 @@ func cliHandle(conn net.Conn) {
resp.Body.Close() resp.Body.Close()
c = NewWSConn(ws) 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) sc := gosocks5.ClientConn(c, clientConfig)

230
http.go Normal file
View File

@ -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)
}

21
main.go
View File

@ -9,13 +9,13 @@ import (
) )
var ( var (
Laddr, Saddr, Proxy string Laddr, Saddr, Proxy string
Websocket bool UseWebsocket, UseHttp bool
Shadows bool Shadows bool
SMethod, SPassword string SMethod, SPassword string
Method, Password string Method, Password string
CertFile, KeyFile string CertFile, KeyFile string
PrintVersion bool PrintVersion bool
) )
func init() { func init() {
@ -27,7 +27,8 @@ func init() {
flag.StringVar(&CertFile, "cert", "", "cert file for tls") flag.StringVar(&CertFile, "cert", "", "cert file for tls")
flag.StringVar(&KeyFile, "key", "", "key file for tls") flag.StringVar(&KeyFile, "key", "", "key file for tls")
flag.BoolVar(&Shadows, "ss", false, "run as shadowsocks server") 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(&SMethod, "sm", "rc4-md5", "shadowsocks cipher method")
flag.StringVar(&SPassword, "sp", "ginuerzh@gmail.com", "shadowsocks cipher password") flag.StringVar(&SPassword, "sp", "ginuerzh@gmail.com", "shadowsocks cipher password")
flag.BoolVar(&PrintVersion, "v", false, "print version") flag.BoolVar(&PrintVersion, "v", false, "print version")
@ -50,8 +51,10 @@ func main() {
if len(Saddr) == 0 { if len(Saddr) == 0 {
var server Server var server Server
if Websocket { if UseWebsocket {
server = &WSServer{Addr: Laddr} server = &WSServer{Addr: Laddr}
} else if UseHttp {
server = &HttpServer{Addr: Laddr}
} else { } else {
server = &Socks5Server{Addr: Laddr} server = &Socks5Server{Addr: Laddr}
} }

View File

@ -5,7 +5,7 @@ import (
) )
const ( const (
Version = "1.3" Version = "1.4"
) )
func printVersion() { func printVersion() {