gost_software/util.go
2015-06-09 12:01:16 +08:00

254 lines
4.9 KiB
Go

package main
import (
"bufio"
//"bytes"
"encoding/base64"
"errors"
"github.com/ginuerzh/gosocks5"
"io"
//"log"
"net"
"net/http"
"net/url"
"strconv"
"strings"
)
const (
MethodTLS uint8 = 0x80 + iota
MethodAES128
MethodAES192
MethodAES256
MethodDES
MethodBF
MethodCAST5
MethodRC4MD5
MethodRC4
MethodTable
)
var Methods = map[uint8]string{
gosocks5.MethodNoAuth: "", // 0x00
MethodTLS: "tls", // 0x80
MethodAES128: "aes-128-cfb", // 0x81
MethodAES192: "aes-192-cfb", // 0x82
MethodAES256: "aes-256-cfb", // 0x83
MethodDES: "des-cfb", // 0x84
MethodBF: "bf-cfb", // 0x85
MethodCAST5: "cast5-cfb", // 0x86
MethodRC4MD5: "rc4-md5", // 8x87
MethodRC4: "rc4", // 0x88
MethodTable: "table", // 0x89
}
func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
host, port, _ := net.SplitHostPort(addr.String())
p, _ := strconv.Atoi(port)
return &gosocks5.Addr{
Type: gosocks5.AddrIPv4,
Host: host,
Port: uint16(p),
}
}
func dial(addr string) (net.Conn, error) {
taddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
return net.DialTCP("tcp", nil, taddr)
}
func connect(addr string) (net.Conn, error) {
if !strings.Contains(addr, ":") {
addr += ":80"
}
if proxyURL == nil {
return dial(addr)
}
switch proxyURL.Scheme {
case "socks": // socks5 proxy
return connectSocks5Proxy(addr)
case "http": // http proxy
fallthrough
default:
return connectHTTPProxy(addr)
}
}
func connectHTTPProxy(addr string) (conn net.Conn, err error) {
conn, err = dial(proxyURL.Host)
if err != nil {
return
}
req := &http.Request{
Method: "CONNECT",
URL: &url.URL{Host: addr},
Host: addr,
Header: make(http.Header),
}
req.Header.Set("Proxy-Connection", "keep-alive")
setBasicAuth(req)
if err = req.Write(conn); err != nil {
conn.Close()
return
}
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
if err != nil {
conn.Close()
return
}
if resp.StatusCode != http.StatusOK {
conn.Close()
//log.Println(resp.Status)
return nil, errors.New(resp.Status)
}
return
}
func connectSocks5Proxy(addr string) (conn net.Conn, err error) {
conn, err = dial(proxyURL.Host)
if err != nil {
return
}
conf := &gosocks5.Config{
Methods: []uint8{gosocks5.MethodNoAuth, gosocks5.MethodUserPass},
MethodSelected: proxyMethodSelected,
}
c := gosocks5.ClientConn(conn, conf)
if err := c.Handleshake(); err != nil {
conn.Close()
return nil, err
}
conn = c
s := strings.Split(addr, ":")
host := s[0]
port := 80
if len(s) == 2 {
n, _ := strconv.ParseUint(s[1], 10, 16)
port = int(n)
}
a := &gosocks5.Addr{
Type: gosocks5.AddrDomain,
Host: host,
Port: uint16(port),
}
if err := gosocks5.NewRequest(gosocks5.CmdConnect, a).Write(conn); err != nil {
conn.Close()
return nil, err
}
rep, err := gosocks5.ReadReply(conn)
if err != nil {
conn.Close()
return nil, err
}
if rep.Rep != gosocks5.Succeeded {
conn.Close()
return nil, errors.New("Socks Failture")
}
return conn, nil
}
func proxyMethodSelected(method uint8, conn net.Conn) (net.Conn, error) {
switch method {
case gosocks5.MethodUserPass:
if proxyURL == nil || proxyURL.User == nil {
return nil, gosocks5.ErrAuthFailure
}
pwd, _ := proxyURL.User.Password()
if err := gosocks5.NewUserPassRequest(gosocks5.UserPassVer,
proxyURL.User.Username(), pwd).Write(conn); err != nil {
return nil, err
}
resp, err := gosocks5.ReadUserPassResponse(conn)
if err != nil {
return nil, err
}
if resp.Status != gosocks5.Succeeded {
return nil, gosocks5.ErrAuthFailure
}
case gosocks5.MethodNoAcceptable:
return nil, gosocks5.ErrBadMethod
//case gosocks5.MethodNoAuth:
}
return conn, nil
}
func setBasicAuth(r *http.Request) {
if proxyURL != nil && proxyURL.User != nil {
r.Header.Set("Proxy-Authorization",
"Basic "+base64.StdEncoding.EncodeToString([]byte(proxyURL.User.String())))
}
}
// based on io.Copy
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
buf := lpool.Take()
defer lpool.put(buf)
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
}