mirror of
https://github.com/wweir/sower.git
synced 2026-10-21 05:51:38 +00:00
Add more protocols support
This commit is contained in:
+48
-23
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user