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()
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)

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 (
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}
}

View File

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