From 302767f68a70e23683470c7dd1683dd759d54cef Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Fri, 27 Mar 2015 17:36:23 +0800 Subject: [PATCH] update --- client.go | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ gost.go | 115 ++----------------------- main.go | 31 +++++-- server.go | 150 ++++++++++++++++++++++++++++++++ socks5.go | 11 --- util.go | 129 ++++++++++++++++++++++++++++ 6 files changed, 559 insertions(+), 127 deletions(-) create mode 100644 client.go create mode 100644 server.go create mode 100644 util.go diff --git a/client.go b/client.go new file mode 100644 index 0000000..898c687 --- /dev/null +++ b/client.go @@ -0,0 +1,250 @@ +package main + +import ( + "bufio" + "bytes" + "github.com/ginuerzh/gosocks5" + "github.com/shadowsocks/shadowsocks-go/shadowsocks" + "io" + "log" + "net" + "net/http" + "strconv" + "strings" +) + +func listenAndServe(addr string, handler func(net.Conn)) error { + laddr, err := net.ResolveTCPAddr("tcp", addr) + if err != nil { + return err + } + + ln, err := net.ListenTCP("tcp", laddr) + if err != nil { + return err + } + defer ln.Close() + + for { + conn, err := ln.AcceptTCP() + if err != nil { + log.Println("accept:", err) + continue + } + //log.Println("accept", conn.RemoteAddr()) + go handler(conn) + } +} + +func handshake(conn net.Conn, methods ...uint8) (method uint8, err error) { + nm := len(methods) + if nm == 0 { + nm = 1 + } + b := make([]byte, 2+nm) + b[0] = Ver5 + b[1] = uint8(nm) + copy(b[2:], methods) + + if _, err = conn.Write(b); err != nil { + return + } + + if _, err = io.ReadFull(conn, b[:2]); err != nil { + return + } + + if b[0] != Ver5 { + err = gosocks5.ErrBadVersion + } + method = b[1] + + return +} + +func cliHandle(conn net.Conn) { + defer conn.Close() + + sconn, err := Connect(Saddr, Proxy) + if err != nil { + return + } + defer sconn.Close() + + method, err := handshake(sconn, MethodAES256, gosocks5.MethodNoAuth) + if err != nil || method == gosocks5.MethodNoAcceptable { + return + } + if method == MethodAES256 { + cipher, _ := shadowsocks.NewCipher(Cipher, Password) + sconn = shadowsocks.NewConn(sconn, cipher) + } + + b := make([]byte, 8192) + + n, err := io.ReadAtLeast(conn, b, 2) + if err != nil { + log.Println(err) + return + } + + if b[0] == gosocks5.Ver5 { + length := 2 + int(b[1]) + if n < length { + if _, err := io.ReadFull(conn, b[n:length]); err != nil { + return + } + } + + if err := gosocks5.WriteMethod(gosocks5.MethodNoAuth, conn); err != nil { + return + } + + handleSocks5(conn, sconn) + return + } + + for { + if bytes.HasSuffix(b[:n], []byte("\r\n\r\n")) { + break + } + + nn, err := conn.Read(b[n:]) + if err != nil { + return + } + n += nn + } + + req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(b[:n]))) + if err != nil { + return + } + handleHttp(req, conn, sconn) +} + +func handleSocks5(conn net.Conn, sconn net.Conn) { + req, err := gosocks5.ReadRequest(conn) + if err != nil { + return + } + + switch req.Cmd { + case gosocks5.CmdConnect, gosocks5.CmdBind: + if err := req.Write(sconn); err != nil { + return + } + Transport(conn, sconn) + case gosocks5.CmdUdp: + if err := req.Write(sconn); err != nil { + return + } + rep, err := gosocks5.ReadReply(sconn) + if err != nil || rep.Rep != gosocks5.Succeeded { + return + } + + uconn, err := net.ListenUDP("udp", nil) + if err != nil { + log.Println(err) + gosocks5.NewReply(Failure, nil).Write(conn) + return + } + defer uconn.Close() + + addr := ToSocksAddr(uconn.LocalAddr()) + addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) + log.Println("udp:", addr) + + rep = gosocks5.NewReply(Succeeded, addr) + if err := rep.Write(conn); err != nil { + log.Println(err) + return + } + + addr.Port = req.Addr.Port + raddr, err := net.ResolveUDPAddr("udp", addr.String()) + if err != nil { + return + } + + cliTunnelUDP(raddr, uconn, conn) + } +} + +func cliTunnelUDP(raddr net.Addr, uconn *net.UDPConn, conn net.Conn) { + go func() { + udp, err := gosocks5.ReadUDPDatagram(uconn) + if err != nil { + log.Println(err) + return + } + udp.Header.Rsv = uint16(len(udp.Data)) + + if err := udp.Write(conn); err != nil { + log.Println(err) + return + } + }() + + for { + udp, err := gosocks5.ReadUDPDatagram(conn) + if err != nil { + log.Println(err) + return + } + udp.Header.Rsv = 0 + buf := &bytes.Buffer{} + udp.Write(buf) + + if _, err := uconn.WriteTo(buf.Bytes(), raddr); err != nil { + log.Println(err) + return + } + } +} + +func handleHttp(req *http.Request, conn net.Conn, sconn net.Conn) { + var host string + var port uint16 + + s := strings.Split(req.Host, ":") + host = s[0] + port = 80 + if len(s) == 2 { + n, _ := strconv.ParseUint(s[1], 10, 16) + port = uint16(n) + } + + addr := &gosocks5.Addr{ + Type: gosocks5.AddrDomain, + Host: host, + Port: port, + } + r := gosocks5.NewRequest(gosocks5.CmdConnect, addr) + if err := r.Write(sconn); err != nil { + return + } + rep, err := gosocks5.ReadReply(sconn) + if err != nil || rep.Rep != gosocks5.Succeeded { + conn.Write([]byte("HTTP/1.1 503 Service unavailable\r\n" + + "Proxy-Agent: gost/1.0\r\n\r\n")) + return + } + + if req.Method == "CONNECT" { + if _, err = conn.Write( + []byte("HTTP/1.1 200 Connection established\r\n" + + "Proxy-Agent: gost/2.0\r\n\r\n")); err != nil { + return + } + } else { + if err := req.Write(sconn); err != nil { + return + } + } + + if err := Transport(conn, sconn); err != nil { + log.Println(err) + } +} diff --git a/gost.go b/gost.go index bf5ffd5..739572c 100644 --- a/gost.go +++ b/gost.go @@ -4,7 +4,7 @@ import ( "bufio" "bytes" //"crypto/tls" - "errors" + //"errors" "io" //"io/ioutil" "log" @@ -16,7 +16,7 @@ import ( "encoding/binary" "fmt" "github.com/shadowsocks/shadowsocks-go/shadowsocks" - "net/url" + //"net/url" "time" ) @@ -90,8 +90,8 @@ func (g *Gost) cli(conn net.Conn) { b := make([]byte, 8192) b[0] = 5 b[1] = 1 - if gost.Cipher { - b[2] = 0x88 + if len(Cipher) > 0 { + b[2] = MethodAES256 } if _, err := sconn.Write(b[:3]); err != nil { @@ -107,7 +107,7 @@ func (g *Gost) cli(conn net.Conn) { } lg.Logln("<<<|", b[:n]) - if b[1] == 0x88 { + if b[1] == MethodAES256 { cipher, _ := shadowsocks.NewCipher("aes-256-cfb", "gost") sconn = shadowsocks.NewConn(sconn, cipher) } @@ -174,7 +174,7 @@ func (g *Gost) srv(conn net.Conn) { } lg.Logln("|<<<", []byte{5, method}) - if method == 0x88 { + if method == MethodAES256 { cipher, _ := shadowsocks.NewCipher("aes-256-cfb", "gost") conn = shadowsocks.NewConn(conn, cipher) } @@ -610,109 +610,6 @@ func shadowTransfer(conn, sconn net.Conn, lg *BufferedLog) { } } -func Connect(addr, proxy string) (net.Conn, error) { - if len(proxy) == 0 { - taddr, err := net.ResolveTCPAddr("tcp", addr) - if err != nil { - log.Println(err) - return nil, err - } - return net.DialTCP("tcp", nil, taddr) - } - - paddr, err := net.ResolveTCPAddr("tcp", proxy) - if err != nil { - return nil, err - } - pconn, err := net.DialTCP("tcp", nil, paddr) - if err != nil { - log.Println(err) - return nil, err - } - - header := http.Header{} - header.Set("Proxy-Connection", "keep-alive") - req := &http.Request{ - Method: "CONNECT", - URL: &url.URL{Host: addr}, - Host: addr, - Header: header, - } - if err := req.Write(pconn); err != nil { - log.Println(err) - pconn.Close() - return nil, err - } - - resp, err := http.ReadResponse(bufio.NewReader(pconn), req) - if err != nil { - log.Println(err) - pconn.Close() - return nil, err - } - if resp.StatusCode != http.StatusOK { - pconn.Close() - return nil, errors.New(resp.Status) - } - - return pconn, nil -} - -func Copy(dst io.Writer, src io.Reader) (written int64, err error) { - buf := make([]byte, 32*1024) - for { - nr, er := src.Read(buf) - //log.Println("cp r", nr, er) - if nr > 0 { - nw, ew := dst.Write(buf[:nr]) - //log.Println("cp w", nw, ew) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - /* - if nr != nw { - err = io.ErrShortWrite - break - } - */ - } - if er == io.EOF { - break - } - if er != nil { - err = er - break - } - } - return -} - -func Pipe(src io.Reader, dst io.Writer, c chan<- error) { - _, err := Copy(dst, src) - c <- err -} - -func Transport(conn, conn2 net.Conn) (err error) { - rChan := make(chan error, 1) - wChan := make(chan error, 1) - - go Pipe(conn, conn2, wChan) - go Pipe(conn2, conn, rChan) - - select { - case err = <-wChan: - //log.Println("w exit", err) - case err = <-rChan: - //log.Println("r exit", err) - } - - return -} - func getRequest(conn net.Conn) (addrType uint8, addr string, port uint16, extra []byte, err error) { const ( idType = 0 // address type index diff --git a/main.go b/main.go index 9ceb6f8..2193009 100644 --- a/main.go +++ b/main.go @@ -3,17 +3,23 @@ package main import ( "flag" + "github.com/ginuerzh/gosocks5" "log" ) -var gost Gost +var ( + Laddr, Saddr, Proxy string + Shadows bool + Cipher, Password string +) func init() { - flag.StringVar(&gost.Proxy, "P", "", "proxy for forward") - flag.StringVar(&gost.Saddr, "S", "", "the server that connecting to") - flag.StringVar(&gost.Laddr, "L", ":8080", "listen address") - flag.BoolVar(&gost.Cipher, "cipher", true, "cipher transfer data") - flag.BoolVar(&gost.Shadows, "ss", false, "shadowsocks compatible") + flag.StringVar(&Proxy, "P", "", "proxy for forward") + flag.StringVar(&Saddr, "S", "", "the server that connecting to") + flag.StringVar(&Laddr, "L", ":8080", "listen address") + flag.StringVar(&Cipher, "cipher", "rc4-md5", "cipher method") + flag.StringVar(&Password, "password", "20150327", "cipher password") + flag.BoolVar(&Shadows, "ss", false, "shadowsocks compatible") flag.BoolVar(&Debug, "d", false, "debug option") flag.Parse() @@ -21,5 +27,16 @@ func init() { } func main() { - log.Fatal(gost.Run()) + //log.Fatal(gost.Run()) + if len(Saddr) == 0 { + srv := &gosocks5.Server{ + Addr: Laddr, + SelectMethod: selectMethod, + Handle: srvHandle, + } + log.Fatal(srv.ListenAndServe()) + return + } + + log.Fatal(listenAndServe(Laddr, cliHandle)) } diff --git a/server.go b/server.go new file mode 100644 index 0000000..45c469a --- /dev/null +++ b/server.go @@ -0,0 +1,150 @@ +package main + +import ( + "github.com/ginuerzh/gosocks5" + "github.com/shadowsocks/shadowsocks-go/shadowsocks" + "net" + //"strconv" + "log" +) + +const ( + MethodAES256 uint8 = 0x88 +) + +func selectMethod(methods ...uint8) uint8 { + for _, method := range methods { + if method == MethodAES256 { + return method + } + } + return gosocks5.MethodNoAcceptable +} + +func srvHandle(conn net.Conn, method uint8) { + defer conn.Close() + + if method == gosocks5.MethodNoAcceptable { + return + } + + if method == MethodAES256 { + cipher, _ := shadowsocks.NewCipher(Cipher, Password) + conn = shadowsocks.NewConn(conn, cipher) + } + + req, err := gosocks5.ReadRequest(conn) + if err != nil { + log.Println(err) + return + } + + switch req.Cmd { + case gosocks5.CmdConnect: + log.Println("connect", req.Addr.String()) + tconn, err := Connect(req.Addr.String(), Proxy) + if err != nil { + gosocks5.NewReply(gosocks5.HostUnreachable, nil).Write(conn) + return + } + defer tconn.Close() + + rep := gosocks5.NewReply(gosocks5.Succeeded, nil) + if err := rep.Write(conn); err != nil { + return + } + + if err := Transport(conn, tconn); err != nil { + log.Println(err) + } + case gosocks5.CmdBind: + l, err := net.ListenTCP("tcp", nil) + if err != nil { + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return + } + + addr := ToSocksAddr(l.Addr()) + addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) + log.Println("bind:", addr) + rep := gosocks5.NewReply(gosocks5.Succeeded, addr) + if err := rep.Write(conn); err != nil { + return + } + + tconn, err := l.AcceptTCP() + if err != nil { + log.Println("accept:", err) + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return + } + defer tconn.Close() + l.Close() + + addr = ToSocksAddr(tconn.RemoteAddr()) + rep = gosocks5.NewReply(gosocks5.Succeeded, addr) + if err := rep.Write(conn); err != nil { + log.Println(err) + return + } + + if err := Transport(conn, tconn); err != nil { + log.Println(err) + } + case gosocks5.CmdUdp: + uconn, err := net.ListenUDP("udp", nil) + if err != nil { + log.Println(err) + gosocks5.NewReply(Failure, nil).Write(conn) + return + } + defer uconn.Close() + + addr := ToSocksAddr(uconn.LocalAddr()) + addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) + log.Println("udp:", addr) + rep := gosocks5.NewReply(Succeeded, addr) + if err := rep.Write(conn); err != nil { + log.Println(err) + return + } + srvTunnelUDP(conn, uconn) + } +} + +func srvTunnelUDP(conn net.Conn, uconn *net.UDPConn) { + go func() { + b := make([]byte, 65535) + n, addr, err := uconn.ReadFromUDP(b) + if err != nil { + log.Println(err) + return + } + + udp := gosocks5.NewUDPDatagram( + gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) + if err := udp.Write(conn); err != nil { + log.Println(err) + return + } + }() + + for { + ud, err := gosocks5.ReadUDPDatagram(conn) + if err != nil { + log.Println(err) + return + } + + addr, err := net.ResolveUDPAddr("udp", ud.Header.Addr.String()) + if err != nil { + log.Println(err) + continue // drop silently + } + + if _, err := uconn.WriteToUDP(ud.Data, addr); err != nil { + log.Println(err) + return + } + } +} diff --git a/socks5.go b/socks5.go index 51f49db..716f583 100644 --- a/socks5.go +++ b/socks5.go @@ -52,17 +52,6 @@ var ( ErrBadFormat = errors.New("Bad format") ErrBadAddrType = errors.New("Bad address type") ErrShortBuffer = errors.New("Short buffer") - - cmdErrMap = map[uint8]error{ - Failure: errors.New("General SOCKS server failure"), - NotAllowed: errors.New("Connection not allowed by ruleset"), - NetUnreachable: errors.New("Network unreachable"), - HostUnreachable: errors.New("Host unreachable"), - ConnRefused: errors.New("Connection refused"), - TTLExpired: errors.New("TTL expired"), - CmdUnsupported: errors.New("Command not supported"), - AddrUnsupported: errors.New("Address type not supported"), - } ) /* diff --git a/util.go b/util.go new file mode 100644 index 0000000..44aa0f6 --- /dev/null +++ b/util.go @@ -0,0 +1,129 @@ +package main + +import ( + "bufio" + //"bytes" + "errors" + "github.com/ginuerzh/gosocks5" + "io" + "log" + "net" + "net/http" + "net/url" + "strconv" +) + +func ToSocksAddr(addr net.Addr) *gosocks5.Addr { + host, port, _ := net.SplitHostPort(addr.String()) + p, _ := strconv.Atoi(port) + + return &gosocks5.Addr{ + Type: AddrIPv4, + Host: host, + Port: uint16(p), + } +} + +func Connect(addr, proxy string) (net.Conn, error) { + if len(proxy) == 0 { + taddr, err := net.ResolveTCPAddr("tcp", addr) + if err != nil { + log.Println(err) + return nil, err + } + return net.DialTCP("tcp", nil, taddr) + } + + paddr, err := net.ResolveTCPAddr("tcp", proxy) + if err != nil { + return nil, err + } + pconn, err := net.DialTCP("tcp", nil, paddr) + if err != nil { + log.Println(err) + return nil, err + } + + header := http.Header{} + header.Set("Proxy-Connection", "keep-alive") + req := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Host: addr}, + Host: addr, + Header: header, + } + if err := req.Write(pconn); err != nil { + log.Println(err) + pconn.Close() + return nil, err + } + + resp, err := http.ReadResponse(bufio.NewReader(pconn), req) + if err != nil { + log.Println(err) + pconn.Close() + return nil, err + } + if resp.StatusCode != http.StatusOK { + pconn.Close() + return nil, errors.New(resp.Status) + } + + return pconn, nil +} + +// based on io.Copy +func Copy(dst io.Writer, src io.Reader) (written int64, err error) { + buf := make([]byte, 32*1024) + for { + nr, er := src.Read(buf) + //log.Println("cp r", nr, er) + if nr > 0 { + nw, ew := dst.Write(buf[:nr]) + //log.Println("cp w", nw, ew) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + /* + if nr != nw { + err = io.ErrShortWrite + break + } + */ + } + if er == io.EOF { + break + } + if er != nil { + err = er + break + } + } + return +} + +func Pipe(src io.Reader, dst io.Writer, c chan<- error) { + _, err := Copy(dst, src) + c <- err +} + +func Transport(conn, conn2 net.Conn) (err error) { + rChan := make(chan error, 1) + wChan := make(chan error, 1) + + go Pipe(conn, conn2, wChan) + go Pipe(conn2, conn, rChan) + + select { + case err = <-wChan: + //log.Println("w exit", err) + case err = <-rChan: + //log.Println("r exit", err) + } + + return +}