add shadowsocks compatible

This commit is contained in:
rui.zheng 2015-03-23 18:17:04 +08:00
parent bdda0cdec9
commit 9ccddf3cf8
3 changed files with 188 additions and 36 deletions

174
gost.go
View File

@ -13,7 +13,10 @@ import (
"strconv" "strconv"
"strings" "strings"
//"sync/atomic" //"sync/atomic"
//"fmt" "encoding/binary"
"fmt"
"github.com/shadowsocks/shadowsocks-go/shadowsocks"
"net/url"
"time" "time"
) )
@ -24,6 +27,7 @@ const (
type Gost struct { type Gost struct {
Laddr, Saddr, Proxy string Laddr, Saddr, Proxy string
Shadows bool // shadowsocks compatible
} }
func (g *Gost) Run() error { func (g *Gost) Run() error {
@ -43,6 +47,7 @@ func (g *Gost) Run() error {
log.Println("accept:", err) log.Println("accept:", err)
continue continue
} }
//log.Println("accept", conn.RemoteAddr().String())
go g.handle(conn) go g.handle(conn)
} }
@ -62,7 +67,13 @@ func (g *Gost) handle(conn net.Conn) {
} }
func (g *Gost) cli(conn net.Conn) { func (g *Gost) cli(conn net.Conn) {
lg := NewLog() lg := NewLog(true)
defer func() {
lg.Logln()
lg.Flush()
}()
lg.Logln("accept", conn.(*net.TCPConn).RemoteAddr().String())
sconn, err := g.connect(g.Saddr) sconn, err := g.connect(g.Saddr)
if err != nil { if err != nil {
@ -80,6 +91,48 @@ func (g *Gost) cli(conn net.Conn) {
} }
lg.Logln(">>>|", []byte{5, 1, 0}) lg.Logln(">>>|", []byte{5, 1, 0})
if g.Shadows {
lg.Logln("shadowsocks, aes-256-cfb")
cipher, _ := shadowsocks.NewCipher("aes-256-cfb", "123456")
conn = shadowsocks.NewConn(conn, cipher)
addr, port, extra, err := getRequest(conn)
if err != nil {
lg.Logln(err)
return
}
lg.Logln(addr, port)
cmd := NewCmd(CmdConnect, AddrDomain, addr, port)
if err = cmd.Write(sconn); err != nil {
lg.Logln(err)
return
}
lg.Logln(">>>|", cmd)
if cmd, err = ReadCmd(sconn); err != nil {
lg.Logln(err)
return
}
lg.Logln("<<<|", cmd)
if cmd.Cmd != Succeeded {
conn.Write([]byte("HTTP/1.1 503 Service unavailable\r\n" +
"Proxy-Agent: gost/1.0\r\n\r\n"))
return
}
if extra != nil {
if _, err := sconn.Write(extra); err != nil {
log.Println(err)
return
}
}
g.transport(conn, sconn)
return
}
b := make([]byte, 8192) b := make([]byte, 8192)
n, err := io.ReadFull(sconn, b[:2]) n, err := io.ReadFull(sconn, b[:2])
@ -94,7 +147,7 @@ func (g *Gost) cli(conn net.Conn) {
lg.Logln(err) lg.Logln(err)
return return
} }
//log.Println(b[:n])
if b[0] == 5 { // socks5,NO AUTHENTICATION if b[0] == 5 { // socks5,NO AUTHENTICATION
lg.Logln("|>>>", b[:n]) lg.Logln("|>>>", b[:n])
@ -130,9 +183,6 @@ func (g *Gost) cli(conn net.Conn) {
} }
lg.Logln("|<<<", cmd) lg.Logln("|<<<", cmd)
lg.Logln()
lg.Flush()
g.transport(conn, sconn) g.transport(conn, sconn)
return return
} }
@ -192,14 +242,17 @@ func (g *Gost) cli(conn net.Conn) {
} }
} }
lg.Logln()
lg.Flush()
g.transport(conn, sconn) g.transport(conn, sconn)
} }
func (g *Gost) srv(conn net.Conn) { func (g *Gost) srv(conn net.Conn) {
b := make([]byte, 8192) b := make([]byte, 8192)
lg := NewLog() lg := NewLog(true)
defer func() {
lg.Logln()
lg.Flush()
}()
lg.Logln("accept", conn.(*net.TCPConn).RemoteAddr().String())
n, err := conn.Read(b) n, err := conn.Read(b)
if err != nil { if err != nil {
@ -306,35 +359,33 @@ func (g *Gost) connect(addr string) (net.Conn, error) {
} }
pconn, err := net.DialTCP("tcp", nil, paddr) pconn, err := net.DialTCP("tcp", nil, paddr)
if err != nil { if err != nil {
log.Println(err)
return nil, err return nil, err
} }
b := make([]byte, 1500) header := http.Header{}
buffer := bytes.NewBuffer(b) header.Set("Proxy-Connection", "keep-alive")
buffer.WriteString("CONNECT " + addr + " HTTP/1.1\r\n") req := &http.Request{
buffer.WriteString("Host: " + addr + "\r\n") Method: "CONNECT",
buffer.WriteString("Proxy-Connection: keep-alive\r\n\r\n") URL: &url.URL{Host: addr},
if _, err = pconn.Write(buffer.Bytes()); err != nil { Host: addr,
Header: header,
}
if err := req.Write(pconn); err != nil {
log.Println(err)
pconn.Close() pconn.Close()
return nil, err return nil, err
} }
r := "" resp, err := http.ReadResponse(bufio.NewReader(pconn), req)
for !strings.HasSuffix(r, "\r\n\r\n") { if err != nil {
n := 0 log.Println(err)
if n, err = pconn.Read(b); err != nil {
pconn.Close() pconn.Close()
return nil, err return nil, err
} }
r += string(b[:n]) if resp.StatusCode != http.StatusOK {
}
log.Println(r)
if !strings.Contains(r, "200") {
log.Println("connection failed:\n", r)
err = errors.New(r)
pconn.Close() pconn.Close()
return nil, err return nil, errors.New(resp.Status)
} }
return pconn, nil return pconn, nil
@ -361,3 +412,72 @@ func (g *Gost) transport(conn, conn2 net.Conn) (err error) {
return return
} }
func getRequest(conn net.Conn) (host string, port uint16, extra []byte, err error) {
const (
idType = 0 // address type index
idIP0 = 1 // ip addres start index
idDmLen = 1 // domain address length index
idDm0 = 2 // domain address start index
typeIPv4 = 1 // type is ipv4 address
typeDm = 3 // type is domain address
typeIPv6 = 4 // type is ipv6 address
lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port
lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port
lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen
)
// buf size should at least have the same size with the largest possible
// request size (when addrType is 3, domain name has at most 256 bytes)
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
buf := make([]byte, 260)
var n int
// read till we get possible domain length field
//ss.SetReadTimeout(conn)
if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil {
log.Println(err)
return
}
log.Println(buf[:n])
reqLen := -1
switch buf[idType] {
case typeIPv4:
reqLen = lenIPv4
case typeIPv6:
reqLen = lenIPv6
case typeDm:
reqLen = int(buf[idDmLen]) + lenDmBase
default:
err = fmt.Errorf("addr type %d not supported", buf[idType])
return
}
if n < reqLen { // rare case
//ss.SetReadTimeout(conn)
if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil {
log.Println(err)
return
}
} else if n > reqLen {
// it's possible to read more than just the request head
extra = buf[reqLen:n]
}
// Return string for typeIP is not most efficient, but browsers (Chrome,
// Safari, Firefox) all seems using typeDm exclusively. So this is not a
// big problem.
switch buf[idType] {
case typeIPv4:
host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String()
case typeIPv6:
host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String()
case typeDm:
host = string(buf[idDm0 : idDm0+buf[idDmLen]])
}
// parse port
port = binary.BigEndian.Uint16(buf[reqLen-2 : reqLen])
return
}

36
log.go
View File

@ -7,16 +7,24 @@ import (
"os" "os"
) )
var (
Debug bool
)
type BufferedLog struct { type BufferedLog struct {
buffer *bytes.Buffer buffer *bytes.Buffer
w io.WriteCloser w io.WriteCloser
} }
func NewLog() *BufferedLog { func NewLog(buffered bool) *BufferedLog {
return &BufferedLog{ log := &BufferedLog{
buffer: &bytes.Buffer{},
w: os.Stdout, w: os.Stdout,
} }
if buffered {
log.buffer = &bytes.Buffer{}
}
return log
} }
func NewFileLog(file *os.File) *BufferedLog { func NewFileLog(file *os.File) *BufferedLog {
@ -27,15 +35,33 @@ func NewFileLog(file *os.File) *BufferedLog {
} }
func (log *BufferedLog) Log(a ...interface{}) (int, error) { func (log *BufferedLog) Log(a ...interface{}) (int, error) {
if !Debug {
return 0, nil
}
if log.buffer != nil {
return fmt.Fprint(log.buffer, a...) return fmt.Fprint(log.buffer, a...)
}
return fmt.Fprint(log.w, a...)
} }
func (log *BufferedLog) Logln(a ...interface{}) (int, error) { func (log *BufferedLog) Logln(a ...interface{}) (int, error) {
if !Debug {
return 0, nil
}
if log.buffer != nil {
return fmt.Fprintln(log.buffer, a...) return fmt.Fprintln(log.buffer, a...)
}
return fmt.Fprintln(log.w, a...)
} }
func (log *BufferedLog) Logf(format string, a ...interface{}) (int, error) { func (log *BufferedLog) Logf(format string, a ...interface{}) (int, error) {
if !Debug {
return 0, nil
}
if log.buffer != nil {
return fmt.Fprintf(log.buffer, format, a...) return fmt.Fprintf(log.buffer, format, a...)
}
return fmt.Fprintf(log.w, format, a...)
} }
func (log *BufferedLog) Flush() error { func (log *BufferedLog) Flush() error {
@ -45,6 +71,10 @@ func (log *BufferedLog) Flush() error {
} }
}() }()
if !Debug || log.buffer == nil {
return nil
}
_, err := log.buffer.WriteTo(log.w) _, err := log.buffer.WriteTo(log.w)
return err return err
} }

View File

@ -12,6 +12,8 @@ func init() {
flag.StringVar(&gost.Proxy, "P", "", "proxy for forward") flag.StringVar(&gost.Proxy, "P", "", "proxy for forward")
flag.StringVar(&gost.Saddr, "S", "", "the server that connecting to") flag.StringVar(&gost.Saddr, "S", "", "the server that connecting to")
flag.StringVar(&gost.Laddr, "L", ":8080", "listen address") flag.StringVar(&gost.Laddr, "L", ":8080", "listen address")
flag.BoolVar(&gost.Shadows, "ss", false, "shadowsocks compatible")
flag.BoolVar(&Debug, "d", false, "debug option")
flag.Parse() flag.Parse()
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)