initial commit
This commit is contained in:
11
server/handler/handler.go
Normal file
11
server/handler/handler.go
Normal file
@ -0,0 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Handler interface {
|
||||
Init(md Metadata) error
|
||||
Handle(context.Context, net.Conn)
|
||||
}
|
233
server/handler/http/handler.go
Normal file
233
server/handler/http/handler.go
Normal file
@ -0,0 +1,233 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-gost/gost/logger"
|
||||
"github.com/go-gost/gost/server/handler"
|
||||
)
|
||||
|
||||
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) 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(conn, req)
|
||||
}
|
||||
|
||||
func (h *Handler) handleRequest(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{},
|
||||
}
|
||||
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")
|
||||
|
||||
/*
|
||||
retries := 1
|
||||
if h.options.Chain != nil && h.options.Chain.Retries > 0 {
|
||||
retries = h.options.Chain.Retries
|
||||
}
|
||||
if h.options.Retries > 0 {
|
||||
retries = h.options.Retries
|
||||
}
|
||||
|
||||
var err error
|
||||
var cc net.Conn
|
||||
var route *Chain
|
||||
for i := 0; i < retries; i++ {
|
||||
route, err = h.options.Chain.selectRouteFor(host)
|
||||
if err != nil {
|
||||
log.Logf("[http] %s -> %s : %s",
|
||||
conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
cc, err = route.Dial(host,
|
||||
TimeoutChainOption(h.options.Timeout),
|
||||
HostsChainOption(h.options.Hosts),
|
||||
ResolverChainOption(h.options.Resolver),
|
||||
)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
*/
|
||||
cc, err := net.Dial("tcp", host)
|
||||
if err != nil {
|
||||
resp.StatusCode = http.StatusServiceUnavailable
|
||||
resp.Write(conn)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
b := []byte("HTTP/1.1 200 Connection established\r\n" +
|
||||
"Proxy-Agent: " + h.md.proxyAgent + "\r\n\r\n")
|
||||
conn.Write(b)
|
||||
} else {
|
||||
req.Header.Del("Proxy-Connection")
|
||||
|
||||
if err = req.Write(cc); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
handler.Transport(conn, cc)
|
||||
}
|
6
server/handler/http/metadata.go
Normal file
6
server/handler/http/metadata.go
Normal file
@ -0,0 +1,6 @@
|
||||
package http
|
||||
|
||||
type metadata struct {
|
||||
addr string
|
||||
proxyAgent string
|
||||
}
|
3
server/handler/metadata.go
Normal file
3
server/handler/metadata.go
Normal file
@ -0,0 +1,3 @@
|
||||
package handler
|
||||
|
||||
type Metadata map[string]string
|
17
server/handler/option.go
Normal file
17
server/handler/option.go
Normal file
@ -0,0 +1,17 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/go-gost/gost/logger"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(opts *Options)
|
||||
|
||||
func LoggerOption(logger logger.Logger) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Logger = logger
|
||||
}
|
||||
}
|
129
server/handler/ss/handler.go
Normal file
129
server/handler/ss/handler.go
Normal file
@ -0,0 +1,129 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gosocks5"
|
||||
"github.com/go-gost/gost/logger"
|
||||
"github.com/go-gost/gost/server/handler"
|
||||
"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
|
||||
}
|
19
server/handler/ss/metadata.go
Normal file
19
server/handler/ss/metadata.go
Normal 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
|
||||
}
|
80
server/handler/ssu/handler.go
Normal file
80
server/handler/ssu/handler.go
Normal file
@ -0,0 +1,80 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gost/logger"
|
||||
"github.com/go-gost/gost/server/handler"
|
||||
"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())
|
||||
}
|
19
server/handler/ssu/metadata.go
Normal file
19
server/handler/ssu/metadata.go
Normal 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
|
||||
}
|
43
server/handler/transport.go
Normal file
43
server/handler/transport.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user