add chain

This commit is contained in:
ginuerzh
2021-10-26 21:07:46 +08:00
parent ce13b2a82a
commit 3351aa5974
78 changed files with 917 additions and 185 deletions

View File

@ -0,0 +1,10 @@
package handler
import (
"context"
"net"
)
type Handler interface {
Handle(context.Context, net.Conn)
}

View File

@ -0,0 +1,225 @@
package http
import (
"bufio"
"context"
"net"
"net/http"
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/components/handler"
"github.com/go-gost/gost/pkg/logger"
)
var (
_ handler.Handler = (*Handler)(nil)
)
type Handler struct {
chain *chain.Chain
logger logger.Logger
md metadata
}
func NewHandler(opts ...handler.Option) *Handler {
options := &handler.Options{}
for _, opt := range opts {
opt(options)
}
return &Handler{
chain: options.Chain,
logger: options.Logger,
}
}
func (h *Handler) Init(md handler.Metadata) error {
return nil
}
func (h *Handler) Handle(ctx context.Context, conn net.Conn) {
defer conn.Close()
req, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil {
h.logger.WithFields(map[string]interface{}{
"src": conn.RemoteAddr(),
"local": conn.LocalAddr(),
}).Error(err)
return
}
defer req.Body.Close()
h.handleRequest(ctx, conn, req)
}
func (h *Handler) handleRequest(ctx context.Context, conn net.Conn, req *http.Request) {
if req == nil {
return
}
/*
// try to get the actual host.
if v := req.Header.Get("Gost-Target"); v != "" {
if h, err := decodeServerName(v); err == nil {
req.Host = h
}
}
*/
host := req.Host
if _, port, _ := net.SplitHostPort(host); port == "" {
host = net.JoinHostPort(host, "80")
}
/*
u, _, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
if u != "" {
u += "@"
}
log.Logf("[http] %s%s -> %s -> %s",
u, conn.RemoteAddr(), h.options.Node.String(), host)
if Debug {
dump, _ := httputil.DumpRequest(req, false)
log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
req.Header.Del("Gost-Target")
*/
resp := &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Header: http.Header{},
}
if h.md.proxyAgent != "" {
resp.Header.Add("Proxy-Agent", h.md.proxyAgent)
}
/*
if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
conn.RemoteAddr(), conn.LocalAddr(), host)
resp.StatusCode = http.StatusForbidden
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
resp.Write(conn)
return
}
*/
/*
if h.options.Bypass.Contains(host) {
resp.StatusCode = http.StatusForbidden
log.Logf("[http] %s - %s bypass %s",
conn.RemoteAddr(), conn.LocalAddr(), host)
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
resp.Write(conn)
return
}
*/
/*
if !h.authenticate(conn, req, resp) {
return
}
*/
if req.Method == "PRI" ||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
resp.StatusCode = http.StatusBadRequest
/*
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s",
conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
*/
resp.Write(conn)
return
}
req.Header.Del("Proxy-Authorization")
cc, err := h.dial(ctx, host)
if err != nil {
resp.StatusCode = http.StatusServiceUnavailable
/*
if Debug {
dump, _ := httputil.DumpResponse(resp, false)
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
}
*/
resp.Write(conn)
return
}
defer cc.Close()
if req.Method == http.MethodConnect {
resp.StatusCode = http.StatusOK
resp.Status = "200 Connection established"
resp.Write(conn)
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
return
}
}
handler.Transport(conn, cc)
}
func (h *Handler) dial(ctx context.Context, addr string) (conn net.Conn, err error) {
count := h.md.retryCount + 1
if count <= 0 {
count = 1
}
for i := 0; i < count; i++ {
route := h.chain.GetRoute()
/*
buf := bytes.Buffer{}
fmt.Fprintf(&buf, "%s -> %s -> ",
conn.RemoteAddr(), h.options.Node.String())
for _, nd := range route.route {
fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String())
}
fmt.Fprintf(&buf, "%s", host)
log.Log("[route]", buf.String())
*/
/*
// forward http request
lastNode := route.LastNode()
if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
err = h.forwardRequest(conn, req, route)
if err == nil {
return
}
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
continue
}
*/
conn, err = route.Dial(ctx, "tcp", addr)
if err != nil {
continue
}
}
return
}

View File

@ -0,0 +1,7 @@
package http
type metadata struct {
addr string
proxyAgent string
retryCount int
}

View File

@ -0,0 +1,3 @@
package handler
type Metadata map[string]string

View File

@ -0,0 +1,25 @@
package handler
import (
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/logger"
)
type Options struct {
Chain *chain.Chain
Logger logger.Logger
}
type Option func(opts *Options)
func LoggerOption(logger logger.Logger) Option {
return func(opts *Options) {
opts.Logger = logger
}
}
func ChainOption(chain *chain.Chain) Option {
return func(opts *Options) {
opts.Chain = chain
}
}

View File

@ -0,0 +1,129 @@
package ss
import (
"bytes"
"context"
"net"
"time"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/pkg/components/handler"
"github.com/go-gost/gost/pkg/logger"
"github.com/shadowsocks/go-shadowsocks2/core"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)
var (
_ handler.Handler = (*Handler)(nil)
)
type Handler struct {
logger logger.Logger
md metadata
}
func NewHandler(opts ...handler.Option) *Handler {
options := &handler.Options{}
for _, opt := range opts {
opt(options)
}
return &Handler{
logger: options.Logger,
}
}
func (h *Handler) Init(md handler.Metadata) (err error) {
h.md, err = h.parseMetadata(md)
if err != nil {
return
}
return nil
}
func (h *Handler) Handle(ctx context.Context, conn net.Conn) {
defer conn.Close()
if h.md.cipher != nil {
conn = &shadowConn{
Conn: h.md.cipher.StreamConn(conn),
}
}
if h.md.readTimeout > 0 {
conn.SetReadDeadline(time.Now().Add(h.md.readTimeout))
}
addr := &gosocks5.Addr{}
_, err := addr.ReadFrom(conn)
if err != nil {
h.logger.Error(err)
return
}
conn.SetReadDeadline(time.Time{})
host := addr.String()
cc, err := net.Dial("tcp", host)
if err != nil {
return
}
defer cc.Close()
handler.Transport(conn, cc)
}
func (h *Handler) parseMetadata(md handler.Metadata) (m metadata, err error) {
m.cipher, err = h.initCipher(md[method], md[password], md[key])
if err != nil {
return
}
if v, ok := md[readTimeout]; ok {
m.readTimeout, _ = time.ParseDuration(v)
}
return
}
func (h *Handler) initCipher(method, password string, key string) (core.Cipher, error) {
if method == "" && password == "" {
return nil, nil
}
c, _ := ss.NewCipher(method, password)
if c != nil {
return &shadowCipher{cipher: c}, nil
}
return core.PickCipher(method, []byte(key), password)
}
type shadowCipher struct {
cipher *ss.Cipher
}
func (c *shadowCipher) StreamConn(conn net.Conn) net.Conn {
return ss.NewConn(conn, c.cipher.Copy())
}
func (c *shadowCipher) PacketConn(conn net.PacketConn) net.PacketConn {
return ss.NewSecurePacketConn(conn, c.cipher.Copy())
}
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
// we wrap around it to make io.Copy happy.
type shadowConn struct {
net.Conn
wbuf bytes.Buffer
}
func (c *shadowConn) Write(b []byte) (n int, err error) {
n = len(b) // force byte length consistent
if c.wbuf.Len() > 0 {
c.wbuf.Write(b) // append the data to the cached header
_, err = c.Conn.Write(c.wbuf.Bytes())
c.wbuf.Reset()
return
}
_, err = c.Conn.Write(b)
return
}

View File

@ -0,0 +1,19 @@
package ss
import (
"time"
"github.com/shadowsocks/go-shadowsocks2/core"
)
const (
method = "method"
password = "password"
key = "key"
readTimeout = "readTimeout"
)
type metadata struct {
cipher core.Cipher
readTimeout time.Duration
}

View File

@ -0,0 +1,80 @@
package ss
import (
"context"
"net"
"time"
"github.com/go-gost/gost/pkg/components/handler"
"github.com/go-gost/gost/pkg/logger"
"github.com/shadowsocks/go-shadowsocks2/core"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)
var (
_ handler.Handler = (*Handler)(nil)
)
type Handler struct {
logger logger.Logger
md metadata
}
func NewHandler(opts ...handler.Option) *Handler {
options := &handler.Options{}
for _, opt := range opts {
opt(options)
}
return &Handler{
logger: options.Logger,
}
}
func (h *Handler) Init(md handler.Metadata) (err error) {
h.md, err = h.parseMetadata(md)
if err != nil {
return
}
return nil
}
func (h *Handler) Handle(ctx context.Context, conn net.Conn) {
defer conn.Close()
}
func (h *Handler) parseMetadata(md handler.Metadata) (m metadata, err error) {
m.cipher, err = h.initCipher(md[method], md[password], md[key])
if err != nil {
return
}
if v, ok := md[readTimeout]; ok {
m.readTimeout, _ = time.ParseDuration(v)
}
return
}
func (h *Handler) initCipher(method, password string, key string) (core.Cipher, error) {
if method == "" && password == "" {
return nil, nil
}
c, _ := ss.NewCipher(method, password)
if c != nil {
return &shadowCipher{cipher: c}, nil
}
return core.PickCipher(method, []byte(key), password)
}
type shadowCipher struct {
cipher *ss.Cipher
}
func (c *shadowCipher) StreamConn(conn net.Conn) net.Conn {
return ss.NewConn(conn, c.cipher.Copy())
}
func (c *shadowCipher) PacketConn(conn net.PacketConn) net.PacketConn {
return ss.NewSecurePacketConn(conn, c.cipher.Copy())
}

View File

@ -0,0 +1,19 @@
package ss
import (
"time"
"github.com/shadowsocks/go-shadowsocks2/core"
)
const (
method = "method"
password = "password"
key = "key"
readTimeout = "readTimeout"
)
type metadata struct {
cipher core.Cipher
readTimeout time.Duration
}

View File

@ -0,0 +1,43 @@
package handler
import (
"io"
"sync"
)
const (
poolBufferSize = 32 * 1024
)
var (
pool = sync.Pool{
New: func() interface{} {
return make([]byte, poolBufferSize)
},
}
)
func Transport(rw1, rw2 io.ReadWriter) error {
errc := make(chan error, 1)
go func() {
errc <- copyBuffer(rw1, rw2)
}()
go func() {
errc <- copyBuffer(rw2, rw1)
}()
err := <-errc
if err != nil && err == io.EOF {
err = nil
}
return err
}
func copyBuffer(dst io.Writer, src io.Reader) error {
buf := pool.Get().([]byte)
defer pool.Put(buf)
_, err := io.CopyBuffer(dst, src, buf)
return err
}