mirror of https://github.com/ginuerzh/gost
6 changed files with 559 additions and 127 deletions
@ -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) |
||||
|
} |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
Loading…
Reference in new issue