Add more protocols support

This commit is contained in:
wweir
2019-03-30 12:31:02 +08:00
parent a6fd60b5a2
commit d3664dc2b9
7 changed files with 206 additions and 76 deletions
+48 -23
View File
@@ -4,16 +4,26 @@ import (
"net"
"github.com/golang/glog"
"github.com/wweir/sower/proxy/parser"
"github.com/wweir/sower/proxy/shadow"
"github.com/wweir/sower/proxy/socks5"
"github.com/wweir/sower/proxy/transport"
)
func StartClient(tran transport.Transport, isSocks5 bool, server, cipher, password, listenIP string) {
connCh := listenLocal(listenIP, []string{"80", "443"})
conn80 := listenLocal(listenIP, "80")
conn443 := listenLocal(listenIP, "443")
var isHttp bool
var conn net.Conn
glog.Infoln("Client started.")
for {
conn := <-connCh
select {
case conn = <-conn80:
isHttp = true
case conn = <-conn443:
isHttp = false
}
resolveAddr(&server)
glog.V(1).Infof("new conn from (%s) to (%s)", conn.RemoteAddr(), server)
@@ -25,41 +35,56 @@ func StartClient(tran transport.Transport, isSocks5 bool, server, cipher, passwo
continue
}
if isSocks5 {
if rc, conn, err = buildSocks5Conn(rc, conn); err != nil {
if !isSocks5 {
rc = shadow.Shadow(rc, cipher, password)
} else if isHttp {
conn, host, port, err := parser.ParseHttpAddr(conn)
if err != nil {
conn.Close()
rc.Close()
glog.Errorln(err)
continue
}
rc = socks5.ToSocks5(rc, host, port)
} else {
rc = shadow.Shadow(rc, cipher, password)
conn, host, err := parser.ParseHttpsHost(conn)
if err != nil {
conn.Close()
rc.Close()
glog.Errorln(err)
continue
}
rc = socks5.ToSocks5(rc, host, "443")
}
go relay(conn, rc)
}
}
func listenLocal(listenIP string, ports []string) <-chan net.Conn {
func listenLocal(listenIP string, port string) <-chan net.Conn {
connCh := make(chan net.Conn, 10)
for i := range ports {
go func(port string) {
ln, err := net.Listen("tcp", net.JoinHostPort(listenIP, port))
go func() {
ln, err := net.Listen("tcp", net.JoinHostPort(listenIP, port))
if err != nil {
glog.Fatalln(err)
}
for {
conn, err := ln.Accept()
if err != nil {
glog.Fatalln(err)
glog.Errorln("accept", listenIP+port, "fail:", err)
continue
}
for {
conn, err := ln.Accept()
if err != nil {
glog.Errorln("accept", listenIP+port, "fail:", err)
continue
}
conn.(*net.TCPConn).SetKeepAlive(true)
connCh <- conn
}
}()
conn.(*net.TCPConn).SetKeepAlive(true)
connCh <- conn
}
}(ports[i])
}
glog.Infoln("listening ports:", ports)
glog.Infoln("listening port:", port)
return connCh
}
+14 -4
View File
@@ -10,7 +10,9 @@ import (
"time"
"github.com/golang/glog"
"github.com/wweir/sower/proxy/parser"
"github.com/wweir/sower/proxy/shadow"
"github.com/wweir/sower/proxy/socks5"
"github.com/wweir/sower/proxy/transport"
)
@@ -56,6 +58,7 @@ func httpProxy(w http.ResponseWriter, r *http.Request,
return nil, err
}
conn = parser.NewHttpProtocol(conn)
return shadow.Shadow(conn, cipher, password), nil
}
}
@@ -103,12 +106,19 @@ func httpsProxy(w http.ResponseWriter, r *http.Request,
return
}
host, port, err := net.SplitHostPort(r.Host)
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
conn.Close()
glog.Errorln("serve https proxy, dial remote fail:", err)
return
}
if isSocks5 {
if rc, conn, err = buildSocks5Conn(rc, conn); err != nil {
glog.Errorln(err)
return
}
rc = socks5.ToSocks5(rc, host, port)
} else {
rc = parser.NewHttpsProtocol(rc, port)
rc = shadow.Shadow(rc, cipher, password)
}
+12
View File
@@ -0,0 +1,12 @@
// Package parser transter conn to be a parser conn
//
// init request payload:
// <type>(1) + <size>(2))(+Overhead) + <data>(size+Overhead)
// data definition:
// 0x00(any): [size](1) + [addr:port] + content
// 0x01(http): content
// 0x02(https): [port](2) + content
//
// init response payload:
// ([status code](2) + <size>(2))(+Overhead) + <content>(size+Overhead)
package parser
+127 -25
View File
@@ -2,45 +2,147 @@ package parser
import (
"bufio"
"fmt"
"io"
"net"
"net/http"
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/wweir/sower/util"
)
func ParseAddr(conn net.Conn) (teeConn *util.TeeConn, addr string, err error) {
teeConn = &util.TeeConn{Conn: conn}
const (
ANY byte = iota
HTTP
HTTPS
)
// Write Addr
type conn struct {
typ byte
domain string
port string
init bool
net.Conn
}
func NewAnyProtocol(c net.Conn, domain, port string) net.Conn {
return &conn{
typ: ANY,
domain: domain,
port: port,
Conn: c,
}
}
func NewHttpProtocol(c net.Conn) net.Conn {
return &conn{
typ: HTTP,
Conn: c,
}
}
func NewHttpsProtocol(c net.Conn, port string) net.Conn {
return &conn{
typ: HTTPS,
port: port,
Conn: c,
}
}
func (c *conn) Write(b []byte) (n int, err error) {
if !c.init {
var pkg []byte
switch c.typ {
case ANY:
pkg = make([]byte, 0, 1+len(c.domain)+1+len(c.port)+len(b))
pkg = append(pkg, ANY)
pkg = append(pkg, byte(len(c.domain)+1+len(c.port)))
pkg = append(pkg, []byte(c.domain+":"+c.port)...)
case HTTP:
pkg = make([]byte, 0, 1+len(b))
pkg = append(pkg, HTTP)
case HTTPS:
pkg = make([]byte, 0, 1+2+len(b))
pkg = append(pkg, HTTPS)
port, _ := strconv.Atoi(c.port)
pkg = append(pkg, byte(port>>8), byte(port))
}
c.init = true
return c.Conn.Write(append(pkg, b...))
}
return c.Conn.Write(b)
}
// Read Addr
func ParseAddr(conn net.Conn) (net.Conn, string, string, error) {
buf := make([]byte, 1)
if _, err := io.ReadFull(conn, buf); err != nil {
return conn, "", "", err
}
switch buf[0] {
case ANY:
if _, err := io.ReadFull(conn, buf); err != nil {
return conn, "", "", err
}
buf = make([]byte, int(buf[0]))
if _, err := io.ReadFull(conn, buf); err != nil {
return conn, "", "", err
}
addr := string(buf)
if idx := strings.LastIndex(addr, ":"); idx != -1 {
return conn, addr[:idx], addr[idx+1:], nil
}
return conn, "", "", errors.New("invalid payload")
case HTTP:
return ParseHttpAddr(conn)
case HTTPS:
buf = make([]byte, 2)
if _, err := io.ReadFull(conn, buf); err != nil {
return conn, "", "", err
}
conn, domain, err := ParseHttpsHost(conn)
return conn, domain, strconv.Itoa(int(buf[0])<<8 + int(buf[1])), err
default:
return conn, "", "", errors.Errorf("not supported type (%v)", buf[0])
}
}
func ParseHttpAddr(conn net.Conn) (net.Conn, string, string, error) {
teeConn := &util.TeeConn{Conn: conn}
teeConn.StartOrReset()
defer teeConn.Stop()
buf := make([]byte, 1)
if n, err := teeConn.Read(buf); err != nil {
return teeConn, "", fmt.Errorf("Read conn fail: %v, readed: %v", err, buf[:n])
}
teeConn.StartOrReset()
// https
if buf[0] == 0x16 { // SSL handleshake
host, _, err := extractSNI(io.Reader(teeConn))
if err != nil {
return teeConn, "", err
}
return teeConn, host + ":443", nil
}
// http
b := bufio.NewReader(teeConn)
resp, err := http.ReadRequest(b)
if err != nil {
return teeConn, "", "", err
}
if idx := strings.LastIndex(resp.Host, ":"); idx != -1 {
return teeConn, resp.Host[:idx], resp.Host[idx+1:], nil
}
return teeConn, resp.Host, "80", nil
}
func ParseHttpsHost(conn net.Conn) (net.Conn, string, error) {
teeConn := &util.TeeConn{Conn: conn}
teeConn.StartOrReset()
defer teeConn.Stop()
domain, _, err := extractSNI(io.Reader(teeConn))
if err != nil {
return teeConn, "", err
}
if strings.Contains(resp.Host, ":") {
return teeConn, resp.Host, nil
}
return teeConn, resp.Host + ":80", nil
return teeConn, domain, nil
}
+3 -3
View File
@@ -33,15 +33,15 @@ func StartServer(tran transport.Transport, port, cipher, password string) {
}
func handle(conn net.Conn) {
conn, addr, err := parser.ParseAddr(conn)
conn, host, port, err := parser.ParseAddr(conn)
if err != nil {
conn.Close()
glog.Warningln(err)
return
}
glog.V(1).Infof("new conn from %s to %s", conn.RemoteAddr(), addr)
glog.V(1).Infof("new conn from %s to %s:%s", conn.RemoteAddr(), host, port)
rc, err := net.Dial("tcp", addr)
rc, err := net.Dial("tcp", net.JoinHostPort(host, port))
if err != nil {
conn.Close()
glog.Warningln(err)
+2 -1
View File
@@ -1,4 +1,5 @@
// Package shadow transter conn to be a crypto conn
// support aead mode only
// payload: [size](2+Overhead bytes) + [content](size+Overhead bytes)
// data payload:
// <size>(2+Overhead) + <content>(size+Overhead)
package shadow
-20
View File
@@ -8,8 +8,6 @@ import (
"time"
"github.com/golang/glog"
"github.com/wweir/sower/proxy/parser"
"github.com/wweir/sower/proxy/socks5"
)
// race safe
@@ -26,24 +24,6 @@ func resolveAddr(server *string) {
}
}
func buildSocks5Conn(c, relay net.Conn) (net.Conn, net.Conn, error) {
conn, addr, err := parser.ParseAddr(relay)
if err != nil {
c.Close()
relay.Close()
return nil, nil, err
}
host, port, err := net.SplitHostPort(addr)
if err != nil {
c.Close()
relay.Close()
return nil, nil, err
}
return socks5.ToSocks5(c, host, port), conn, nil
}
func relay(conn1, conn2 net.Conn) {
wg := &sync.WaitGroup{}
exitFlag := new(int32)