mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +08:00
general: add a forwarder struct and changed proxies to use it
This commit is contained in:
parent
2fcef7b00c
commit
0da05ecedd
37
conf.go
37
conf.go
@ -14,14 +14,15 @@ import (
|
||||
var flag = conflag.New()
|
||||
|
||||
var conf struct {
|
||||
Verbose bool
|
||||
Strategy string
|
||||
CheckWebSite string
|
||||
CheckInterval int
|
||||
Listen []string
|
||||
Forward []string
|
||||
RuleFile []string
|
||||
RulesDir string
|
||||
Verbose bool
|
||||
|
||||
Listen []string
|
||||
|
||||
Forward []string
|
||||
StrategyConfig
|
||||
|
||||
RuleFile []string
|
||||
RulesDir string
|
||||
|
||||
DNS string
|
||||
DNSServer []string
|
||||
@ -37,11 +38,13 @@ var conf struct {
|
||||
|
||||
func confInit() {
|
||||
flag.BoolVar(&conf.Verbose, "verbose", false, "verbose mode")
|
||||
flag.StringVar(&conf.Strategy, "strategy", "rr", "forward strategy, default: rr")
|
||||
flag.StringVar(&conf.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
|
||||
flag.IntVar(&conf.CheckInterval, "checkduration", 30, "proxy check interval(seconds)")
|
||||
flag.StringSliceUniqVar(&conf.Listen, "listen", nil, "listen url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS")
|
||||
|
||||
flag.StringSliceUniqVar(&conf.Forward, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]")
|
||||
flag.StringVar(&conf.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr")
|
||||
flag.StringVar(&conf.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
|
||||
flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkduration", 30, "proxy check interval(seconds)")
|
||||
|
||||
flag.StringSliceUniqVar(&conf.RuleFile, "rulefile", nil, "rule file path")
|
||||
flag.StringVar(&conf.RulesDir, "rules-dir", "", "rule file folder")
|
||||
|
||||
@ -119,10 +122,8 @@ func listDir(dirPth string, suffix string) (files []string, err error) {
|
||||
type RuleConf struct {
|
||||
name string
|
||||
|
||||
Forward []string
|
||||
Strategy string
|
||||
CheckWebSite string
|
||||
CheckInterval int
|
||||
Forward []string
|
||||
StrategyConfig
|
||||
|
||||
DNSServer []string
|
||||
IPSet string
|
||||
@ -138,9 +139,9 @@ func NewRuleConfFromFile(ruleFile string) (*RuleConf, error) {
|
||||
|
||||
f := conflag.NewFromFile("rule", ruleFile)
|
||||
f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]")
|
||||
f.StringVar(&p.Strategy, "strategy", "rr", "forward strategy, default: rr")
|
||||
f.StringVar(&p.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
|
||||
f.IntVar(&p.CheckInterval, "checkduration", 30, "proxy check interval(seconds)")
|
||||
f.StringVar(&p.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr")
|
||||
f.StringVar(&p.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
|
||||
f.IntVar(&p.StrategyConfig.CheckInterval, "checkduration", 30, "proxy check interval(seconds)")
|
||||
|
||||
f.StringSliceUniqVar(&p.DNSServer, "dnsserver", nil, "remote dns server")
|
||||
f.StringVar(&p.IPSet, "ipset", "", "ipset name")
|
||||
|
37
main.go
37
main.go
@ -4,7 +4,6 @@ import (
|
||||
stdlog "log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
@ -25,39 +24,28 @@ import (
|
||||
)
|
||||
|
||||
// VERSION .
|
||||
const VERSION = "0.6.6"
|
||||
|
||||
func dialerFromConf() proxy.Dialer {
|
||||
// global forwarders in xx.conf
|
||||
var fwdrs []proxy.Dialer
|
||||
for _, chain := range conf.Forward {
|
||||
var fwdr proxy.Dialer
|
||||
var err error
|
||||
for _, url := range strings.Split(chain, ",") {
|
||||
fwdr, err = proxy.DialerFromURL(url, fwdr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
fwdrs = append(fwdrs, fwdr)
|
||||
}
|
||||
|
||||
return NewStrategyDialer(conf.Strategy, fwdrs, conf.CheckWebSite, conf.CheckInterval)
|
||||
}
|
||||
const VERSION = "0.6.7"
|
||||
|
||||
func main() {
|
||||
|
||||
// Config
|
||||
confInit()
|
||||
|
||||
// Log
|
||||
log.F = func(f string, v ...interface{}) {
|
||||
if conf.Verbose {
|
||||
stdlog.Printf(f, v...)
|
||||
}
|
||||
}
|
||||
|
||||
dialer := NewRuleDialer(conf.rules, dialerFromConf())
|
||||
ipsetM, _ := NewIPSetManager(conf.IPSet, conf.rules)
|
||||
if conf.DNS != "" {
|
||||
// Forwarder
|
||||
dialer := NewRuleDialer(conf.rules, StrategyDialer(conf.Forward, &conf.StrategyConfig))
|
||||
|
||||
// IPSet manager
|
||||
ipsetM, _ := NewIPSetManager(conf.IPSet, conf.rules)
|
||||
|
||||
// DNS Server
|
||||
if conf.DNS != "" {
|
||||
dnscfg := &dns.Config{
|
||||
Timeout: conf.DNSTimeout,
|
||||
MaxTTL: conf.DNSMaxTTL,
|
||||
@ -91,8 +79,9 @@ func main() {
|
||||
go d.ListenAndServe()
|
||||
}
|
||||
|
||||
// Servers
|
||||
for _, listen := range conf.Listen {
|
||||
local, err := proxy.ServerFromURL(listen, dialer)
|
||||
local, err := proxy.ServerFromURL(listen, proxy.NewForwarder(dialer))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
49
proxy/forwarder.go
Normal file
49
proxy/forwarder.go
Normal file
@ -0,0 +1,49 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Forwarder is a forwarder
|
||||
type Forwarder struct {
|
||||
Dialer
|
||||
addr string
|
||||
disabled bool
|
||||
failures int
|
||||
priority int
|
||||
weight int
|
||||
}
|
||||
|
||||
// ForwarderFromURL returns a new forwarder
|
||||
func ForwarderFromURL(s string) (*Forwarder, error) {
|
||||
var d Dialer
|
||||
var err error
|
||||
for _, url := range strings.Split(s, ",") {
|
||||
d, err = DialerFromURL(url, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Forwarder{Dialer: d}, nil
|
||||
}
|
||||
|
||||
// NewForwarder .
|
||||
func NewForwarder(dialer Dialer) *Forwarder {
|
||||
return &Forwarder{Dialer: dialer, addr: dialer.Addr()}
|
||||
}
|
||||
|
||||
// Addr .
|
||||
func (f *Forwarder) Addr() string {
|
||||
return f.addr
|
||||
}
|
||||
|
||||
// Enable .
|
||||
func (f *Forwarder) Enable(b bool) {
|
||||
f.disabled = !b
|
||||
}
|
||||
|
||||
// Enabled .
|
||||
func (f *Forwarder) Enabled() bool {
|
||||
return !f.disabled
|
||||
}
|
90
proxy/http/dialer.go
Normal file
90
proxy/http/dialer.go
Normal file
@ -0,0 +1,90 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("http", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*HTTP
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewHTTP(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{HTTP: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("[http] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n"))
|
||||
buf.Write([]byte("Proxy-Connection: Keep-Alive\r\n"))
|
||||
|
||||
if s.user != "" && s.password != "" {
|
||||
auth := s.user + ":" + s.password
|
||||
buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n"))
|
||||
}
|
||||
|
||||
//header ended
|
||||
buf.Write([]byte("\r\n"))
|
||||
rc.Write(buf.Bytes())
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
_, code, _, ok := parseFirstLine(respTP)
|
||||
if ok && code == "200" {
|
||||
return rc, err
|
||||
} else if code == "407" {
|
||||
log.F("[http] authencation needed by proxy %s", s.addr)
|
||||
} else if code == "405" {
|
||||
log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr)
|
||||
}
|
||||
|
||||
return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code)
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
return nil, nil, errors.New("http client does not support udp")
|
||||
}
|
@ -4,38 +4,23 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// HTTP struct
|
||||
type HTTP struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
user string
|
||||
password string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("http", NewHTTPDialer)
|
||||
proxy.RegisterServer("http", NewHTTPServer)
|
||||
}
|
||||
|
||||
// NewHTTP returns a http proxy.
|
||||
func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) {
|
||||
// NewHTTP returns a http base struct
|
||||
func NewHTTP(s string) (*HTTP, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -47,7 +32,6 @@ func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) {
|
||||
pass, _ := u.User.Password()
|
||||
|
||||
h := &HTTP{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
user: user,
|
||||
password: pass,
|
||||
@ -56,215 +40,12 @@ func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) {
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// NewHTTPDialer returns a http proxy dialer.
|
||||
func NewHTTPDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewHTTP(s, dialer)
|
||||
}
|
||||
|
||||
// NewHTTPServer returns a http proxy server.
|
||||
func NewHTTPServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewHTTP(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *HTTP) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.F("listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[http] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve .
|
||||
func (s *HTTP) Serve(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
reqR := bufio.NewReader(c)
|
||||
reqTP := textproto.NewReader(reqR)
|
||||
method, requestURI, proto, ok := parseFirstLine(reqTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if method == "CONNECT" {
|
||||
s.servHTTPS(method, requestURI, proto, c)
|
||||
return
|
||||
}
|
||||
|
||||
reqHeader, err := reqTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("read header error:%s", err)
|
||||
return
|
||||
}
|
||||
cleanHeaders(reqHeader)
|
||||
|
||||
// tell the remote server not to keep alive
|
||||
reqHeader.Set("Connection", "close")
|
||||
|
||||
u, err := url.ParseRequestURI(requestURI)
|
||||
if err != nil {
|
||||
log.F("[http] parse request url error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
var tgt = u.Host
|
||||
if !strings.Contains(u.Host, ":") {
|
||||
tgt += ":80"
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("tcp", tgt)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
// GET http://example.com/a/index.htm HTTP/1.1 -->
|
||||
// GET /a/index.htm HTTP/1.1
|
||||
u.Scheme = ""
|
||||
u.Host = ""
|
||||
uri := u.String()
|
||||
|
||||
var reqBuf bytes.Buffer
|
||||
writeFirstLine(method, uri, proto, &reqBuf)
|
||||
writeHeaders(reqHeader, &reqBuf)
|
||||
|
||||
// send request to remote server
|
||||
rc.Write(reqBuf.Bytes())
|
||||
|
||||
// copy the left request bytes to remote server. eg. length specificed or chunked body.
|
||||
go func() {
|
||||
if _, err := reqR.Peek(1); err == nil {
|
||||
io.Copy(rc, reqR)
|
||||
rc.SetDeadline(time.Now())
|
||||
c.SetDeadline(time.Now())
|
||||
}
|
||||
}()
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
proto, code, status, ok := parseFirstLine(respTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
respHeader, err := respTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("[http] read header error:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
respHeader.Set("Proxy-Connection", "close")
|
||||
respHeader.Set("Connection", "close")
|
||||
|
||||
var respBuf bytes.Buffer
|
||||
writeFirstLine(proto, code, status, &respBuf)
|
||||
writeHeaders(respHeader, &respBuf)
|
||||
|
||||
log.F("[http] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
c.Write(respBuf.Bytes())
|
||||
|
||||
io.Copy(c, respR)
|
||||
|
||||
}
|
||||
|
||||
func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) {
|
||||
rc, err := s.dialer.Dial("tcp", requestURI)
|
||||
if err != nil {
|
||||
c.Write([]byte(proto))
|
||||
c.Write([]byte(" 502 ERROR\r\n\r\n"))
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
|
||||
|
||||
log.F("[http] %s <-> %s [c]", c.RemoteAddr(), requestURI)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("relay error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *HTTP) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *HTTP) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("[http] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n"))
|
||||
buf.Write([]byte("Proxy-Connection: Keep-Alive\r\n"))
|
||||
|
||||
if s.user != "" && s.password != "" {
|
||||
auth := s.user + ":" + s.password
|
||||
buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n"))
|
||||
}
|
||||
|
||||
//header ended
|
||||
buf.Write([]byte("\r\n"))
|
||||
rc.Write(buf.Bytes())
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
_, code, _, ok := parseFirstLine(respTP)
|
||||
if ok && code == "200" {
|
||||
return rc, err
|
||||
} else if code == "407" {
|
||||
log.F("[http] authencation needed by proxy %s", s.addr)
|
||||
} else if code == "405" {
|
||||
log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr)
|
||||
}
|
||||
|
||||
return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
return nil, nil, errors.New("http client does not support udp")
|
||||
}
|
||||
|
||||
// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts.
|
||||
func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) {
|
||||
line, err := tp.ReadLine()
|
||||
// log.F("first line: %s", line)
|
||||
if err != nil {
|
||||
log.F("[http] read first line error:%s", err)
|
||||
log.F("[http] read first line error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
184
proxy/http/server.go
Normal file
184
proxy/http/server.go
Normal file
@ -0,0 +1,184 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("http", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*HTTP
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewHTTP(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{HTTP: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.F("listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[http] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve .
|
||||
func (s *Server) Serve(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
reqR := bufio.NewReader(c)
|
||||
reqTP := textproto.NewReader(reqR)
|
||||
method, requestURI, proto, ok := parseFirstLine(reqTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if method == "CONNECT" {
|
||||
s.servHTTPS(method, requestURI, proto, c)
|
||||
return
|
||||
}
|
||||
|
||||
reqHeader, err := reqTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("read header error:%s", err)
|
||||
return
|
||||
}
|
||||
cleanHeaders(reqHeader)
|
||||
|
||||
// tell the remote server not to keep alive
|
||||
reqHeader.Set("Connection", "close")
|
||||
|
||||
u, err := url.ParseRequestURI(requestURI)
|
||||
if err != nil {
|
||||
log.F("[http] parse request url error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
var tgt = u.Host
|
||||
if !strings.Contains(u.Host, ":") {
|
||||
tgt += ":80"
|
||||
}
|
||||
|
||||
rc, err := s.Dial("tcp", tgt)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
// GET http://example.com/a/index.htm HTTP/1.1 -->
|
||||
// GET /a/index.htm HTTP/1.1
|
||||
u.Scheme = ""
|
||||
u.Host = ""
|
||||
uri := u.String()
|
||||
|
||||
var reqBuf bytes.Buffer
|
||||
writeFirstLine(method, uri, proto, &reqBuf)
|
||||
writeHeaders(reqHeader, &reqBuf)
|
||||
|
||||
// send request to remote server
|
||||
rc.Write(reqBuf.Bytes())
|
||||
|
||||
// copy the left request bytes to remote server. eg. length specificed or chunked body.
|
||||
go func() {
|
||||
if _, err := reqR.Peek(1); err == nil {
|
||||
io.Copy(rc, reqR)
|
||||
rc.SetDeadline(time.Now())
|
||||
c.SetDeadline(time.Now())
|
||||
}
|
||||
}()
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
proto, code, status, ok := parseFirstLine(respTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
respHeader, err := respTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("[http] read header error:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
respHeader.Set("Proxy-Connection", "close")
|
||||
respHeader.Set("Connection", "close")
|
||||
|
||||
var respBuf bytes.Buffer
|
||||
writeFirstLine(proto, code, status, &respBuf)
|
||||
writeHeaders(respHeader, &respBuf)
|
||||
|
||||
log.F("[http] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
c.Write(respBuf.Bytes())
|
||||
|
||||
io.Copy(c, respR)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) servHTTPS(method, requestURI, proto string, c net.Conn) {
|
||||
rc, err := s.Dial("tcp", requestURI)
|
||||
if err != nil {
|
||||
c.Write([]byte(proto))
|
||||
c.Write([]byte(" 502 ERROR\r\n\r\n"))
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
|
||||
|
||||
log.F("[http] %s <-> %s [c]", c.RemoteAddr(), requestURI)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("relay error: %v", err)
|
||||
}
|
||||
}
|
@ -12,6 +12,10 @@ import (
|
||||
"github.com/nadoo/glider/proxy/socks5"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("mixed", NewMixedProxyServer)
|
||||
}
|
||||
|
||||
// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase.
|
||||
var httpMethods = [...][]byte{
|
||||
[]byte("GET"),
|
||||
@ -24,45 +28,40 @@ var httpMethods = [...][]byte{
|
||||
[]byte("TRACE"),
|
||||
}
|
||||
|
||||
// MixedProxy struct
|
||||
type MixedProxy struct {
|
||||
dialer proxy.Dialer
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*proxy.Forwarder
|
||||
addr string
|
||||
|
||||
http *http.HTTP
|
||||
socks5 *socks5.SOCKS5
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("mixed", NewMixedProxyServer)
|
||||
http *http.Server
|
||||
socks5 *socks5.Server
|
||||
}
|
||||
|
||||
// NewMixedProxy returns a mixed proxy.
|
||||
func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) {
|
||||
func NewMixedProxy(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := &MixedProxy{
|
||||
dialer: dialer,
|
||||
addr: u.Host,
|
||||
p := &Server{
|
||||
Forwarder: f,
|
||||
addr: u.Host,
|
||||
}
|
||||
|
||||
p.http, _ = http.NewHTTP(s, dialer)
|
||||
p.socks5, _ = socks5.NewSOCKS5(s, dialer)
|
||||
p.http, _ = http.NewServer(s, f)
|
||||
p.socks5, _ = socks5.NewServer(s, f)
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewMixedProxyServer returns a mixed proxy server.
|
||||
func NewMixedProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewMixedProxy(s, dialer)
|
||||
func NewMixedProxyServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewMixedProxy(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (p *MixedProxy) ListenAndServe() {
|
||||
func (p *Server) ListenAndServe() {
|
||||
|
||||
go p.socks5.ListenAndServeUDP()
|
||||
|
||||
@ -86,7 +85,7 @@ func (p *MixedProxy) ListenAndServe() {
|
||||
}
|
||||
|
||||
// Serve .
|
||||
func (p *MixedProxy) Serve(c net.Conn) {
|
||||
func (p *Server) Serve(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
@ -16,6 +16,10 @@ import (
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("redir", CreateServer)
|
||||
}
|
||||
|
||||
const (
|
||||
// SO_ORIGINAL_DST from linux/include/uapi/linux/netfilter_ipv4.h
|
||||
SO_ORIGINAL_DST = 80
|
||||
@ -23,40 +27,31 @@ const (
|
||||
IP6T_SO_ORIGINAL_DST = 80
|
||||
)
|
||||
|
||||
// RedirProxy struct
|
||||
type RedirProxy struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
// Server struct
|
||||
type Server struct {
|
||||
addr string
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("redir", NewRedirServer)
|
||||
}
|
||||
|
||||
// NewRedirProxy returns a redirect proxy.
|
||||
func NewRedirProxy(s string, dialer proxy.Dialer) (*RedirProxy, error) {
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr := u.Host
|
||||
r := &RedirProxy{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
}
|
||||
|
||||
return r, nil
|
||||
server := &Server{addr: u.Host, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// NewRedirServer returns a redir server.
|
||||
func NewRedirServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewRedirProxy(s, dialer)
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *RedirProxy) ListenAndServe() {
|
||||
func (s *Server) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[redir] failed to listen on %s: %v", s.addr, err)
|
||||
@ -85,7 +80,7 @@ func (s *RedirProxy) ListenAndServe() {
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("tcp", tgt.String())
|
||||
rc, err := s.Dial("tcp", tgt.String())
|
||||
if err != nil {
|
||||
log.F("[redir] failed to connect to target: %v", err)
|
||||
return
|
@ -15,7 +15,7 @@ type Server interface {
|
||||
}
|
||||
|
||||
// ServerCreator is a function to create proxy servers.
|
||||
type ServerCreator func(s string, dialer Dialer) (Server, error)
|
||||
type ServerCreator func(s string, f *Forwarder) (Server, error)
|
||||
|
||||
var (
|
||||
serverMap = make(map[string]ServerCreator)
|
||||
@ -27,7 +27,7 @@ func RegisterServer(name string, c ServerCreator) {
|
||||
}
|
||||
|
||||
// ServerFromURL calls the registered creator to create proxy servers.
|
||||
func ServerFromURL(s string, dialer Dialer) (Server, error) {
|
||||
func ServerFromURL(s string, f *Forwarder) (Server, error) {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = "mixed://" + s
|
||||
}
|
||||
@ -38,13 +38,13 @@ func ServerFromURL(s string, dialer Dialer) (Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dialer == nil {
|
||||
dialer = Direct
|
||||
if f == nil {
|
||||
f = NewForwarder(Direct)
|
||||
}
|
||||
|
||||
c, ok := serverMap[strings.ToLower(u.Scheme)]
|
||||
if ok {
|
||||
return c(s, dialer)
|
||||
return c(s, f)
|
||||
}
|
||||
|
||||
return nil, errors.New("unknown scheme '" + u.Scheme + "'")
|
||||
|
251
proxy/socks5/dialer.go
Normal file
251
proxy/socks5/dialer.go
Normal file
@ -0,0 +1,251 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("socks5", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*SOCKS5
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
socks, err := NewSOCKS5(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{SOCKS5: socks, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("[socks5]: no support for connection type " + network)
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.connect(c, addr); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// send VER, NMETHODS, METHODS
|
||||
c.Write([]byte{5, 1, 0})
|
||||
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER METHOD
|
||||
if _, err := io.ReadFull(c, buf[:2]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dstAddr := socks.ParseAddr(addr)
|
||||
// write VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
c.Write(append([]byte{5, socks.CmdUDPAssociate, 0}, dstAddr...))
|
||||
|
||||
// read VER REP RSV ATYP BND.ADDR BND.PORT
|
||||
if _, err := io.ReadFull(c, buf[:3]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rep := buf[1]
|
||||
if rep != 0 {
|
||||
log.F("[socks5] server reply: %d, not succeeded", rep)
|
||||
return nil, nil, errors.New("server connect failed")
|
||||
}
|
||||
|
||||
uAddr, err := socks.ReadAddrBuf(c, buf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pc, nextHop, err := s.dialer.DialUDP(network, uAddr.String())
|
||||
if err != nil {
|
||||
log.F("[socks5] dialudp to %s error: %s", uAddr.String(), err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(pc, nextHop, dstAddr, true, c)
|
||||
return pkc, nextHop, err
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *Dialer) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, socks.AuthNone, socks.AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, socks.AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
if buf[1] == socks.AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, socks.ATypIP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, socks.ATypIP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination hostname too long: " + host)
|
||||
}
|
||||
buf = append(buf, socks.ATypDomain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(socks.Errors) {
|
||||
failure = socks.Errors[buf[1]].Error()
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case socks.ATypIP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case socks.ATypIP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case socks.ATypDomain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
214
proxy/socks5/server.go
Normal file
214
proxy/socks5/server.go
Normal file
@ -0,0 +1,214 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("socks5", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*SOCKS5
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewSOCKS5(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{SOCKS5: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves socks5 requests.
|
||||
func (s *Server) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP .
|
||||
func (s *Server) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[socks5] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP .
|
||||
func (s *Server) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
tgt, err := s.handshake(c)
|
||||
if err != nil {
|
||||
// UDP: keep the connection until disconnect then free the UDP socket
|
||||
if err == socks.Errors[9] {
|
||||
buf := []byte{}
|
||||
// block here
|
||||
for {
|
||||
_, err := c.Read(buf)
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
continue
|
||||
}
|
||||
// log.F("[socks5] servetcp udp associate end")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.F("[socks5] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := s.Dial("tcp", tgt.String())
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[socks5] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[socks5] relay error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp requests.
|
||||
func (s *Server) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
log.F("[socks5-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true, nil)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
if c.tgtAddr == nil {
|
||||
log.F("[socks5-udp] can not get target address, not a valid request")
|
||||
continue
|
||||
}
|
||||
|
||||
lpc, nextHop, err := s.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false, nil)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handshake fast-tracks SOCKS initialization to get target address to connect.
|
||||
func (s *Server) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
||||
// Read RFC 1928 for request and reply structure and sizes.
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER, NMETHODS, METHODS
|
||||
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nmethods := buf[1]
|
||||
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// write VER METHOD
|
||||
if _, err := rw.Write([]byte{5, 0}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// read VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
if _, err := io.ReadFull(rw, buf[:3]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := buf[1]
|
||||
addr, err := socks.ReadAddrBuf(rw, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch cmd {
|
||||
case socks.CmdConnect:
|
||||
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded
|
||||
case socks.CmdUDPAssociate:
|
||||
listenAddr := socks.ParseAddr(rw.(net.Conn).LocalAddr().String())
|
||||
_, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded
|
||||
if err != nil {
|
||||
return nil, socks.Errors[7]
|
||||
}
|
||||
err = socks.Errors[9]
|
||||
default:
|
||||
return nil, socks.Errors[7]
|
||||
}
|
||||
|
||||
return addr, err // skip VER, CMD, RSV fields
|
||||
}
|
@ -12,18 +12,9 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// Version is socks5 version number
|
||||
@ -31,20 +22,14 @@ const Version = 5
|
||||
|
||||
// SOCKS5 struct
|
||||
type SOCKS5 struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
user string
|
||||
password string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("socks5", NewSocks5Dialer)
|
||||
proxy.RegisterServer("socks5", NewSocks5Server)
|
||||
}
|
||||
|
||||
// NewSOCKS5 returns a Proxy that makes SOCKS v5 connections to the given address
|
||||
// with an optional username and password. See RFC 1928.
|
||||
func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
||||
func NewSOCKS5(s string) (*SOCKS5, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -56,7 +41,6 @@ func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
||||
pass, _ := u.User.Password()
|
||||
|
||||
h := &SOCKS5{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
user: user,
|
||||
password: pass,
|
||||
@ -64,402 +48,3 @@ func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// NewSocks5Dialer returns a socks5 proxy dialer.
|
||||
func NewSocks5Dialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewSOCKS5(s, dialer)
|
||||
}
|
||||
|
||||
// NewSocks5Server returns a socks5 proxy server.
|
||||
func NewSocks5Server(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewSOCKS5(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe serves socks5 requests.
|
||||
func (s *SOCKS5) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP .
|
||||
func (s *SOCKS5) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[socks5] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP .
|
||||
func (s *SOCKS5) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
tgt, err := s.handshake(c)
|
||||
if err != nil {
|
||||
// UDP: keep the connection until disconnect then free the UDP socket
|
||||
if err == socks.Errors[9] {
|
||||
buf := []byte{}
|
||||
// block here
|
||||
for {
|
||||
_, err := c.Read(buf)
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
continue
|
||||
}
|
||||
// log.F("[socks5] servetcp udp associate end")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.F("[socks5] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("tcp", tgt.String())
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[socks5] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[socks5] relay error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp requests.
|
||||
func (s *SOCKS5) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
log.F("[socks5-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true, nil)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
if c.tgtAddr == nil {
|
||||
log.F("[socks5-udp] can not get target address, not a valid request")
|
||||
continue
|
||||
}
|
||||
|
||||
lpc, nextHop, err := s.dialer.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false, nil)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *SOCKS5) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *SOCKS5) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the SOCKS5 proxy.
|
||||
func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("[socks5]: no support for connection type " + network)
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.connect(c, addr); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// send VER, NMETHODS, METHODS
|
||||
c.Write([]byte{5, 1, 0})
|
||||
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER METHOD
|
||||
if _, err := io.ReadFull(c, buf[:2]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dstAddr := socks.ParseAddr(addr)
|
||||
// write VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
c.Write(append([]byte{5, socks.CmdUDPAssociate, 0}, dstAddr...))
|
||||
|
||||
// read VER REP RSV ATYP BND.ADDR BND.PORT
|
||||
if _, err := io.ReadFull(c, buf[:3]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rep := buf[1]
|
||||
if rep != 0 {
|
||||
log.F("[socks5] server reply: %d, not succeeded", rep)
|
||||
return nil, nil, errors.New("server connect failed")
|
||||
}
|
||||
|
||||
uAddr, err := socks.ReadAddrBuf(c, buf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pc, nextHop, err := s.dialer.DialUDP(network, uAddr.String())
|
||||
if err != nil {
|
||||
log.F("[socks5] dialudp to %s error: %s", uAddr.String(), err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(pc, nextHop, dstAddr, true, c)
|
||||
return pkc, nextHop, err
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, socks.AuthNone, socks.AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, socks.AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
if buf[1] == socks.AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, socks.ATypIP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, socks.ATypIP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination hostname too long: " + host)
|
||||
}
|
||||
buf = append(buf, socks.ATypDomain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(socks.Errors) {
|
||||
failure = socks.Errors[buf[1]].Error()
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case socks.ATypIP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case socks.ATypIP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case socks.ATypDomain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handshake fast-tracks SOCKS initialization to get target address to connect.
|
||||
func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
||||
// Read RFC 1928 for request and reply structure and sizes.
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER, NMETHODS, METHODS
|
||||
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nmethods := buf[1]
|
||||
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// write VER METHOD
|
||||
if _, err := rw.Write([]byte{5, 0}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// read VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
if _, err := io.ReadFull(rw, buf[:3]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := buf[1]
|
||||
addr, err := socks.ReadAddrBuf(rw, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch cmd {
|
||||
case socks.CmdConnect:
|
||||
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded
|
||||
case socks.CmdUDPAssociate:
|
||||
listenAddr := socks.ParseAddr(rw.(net.Conn).LocalAddr().String())
|
||||
_, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded
|
||||
if err != nil {
|
||||
return nil, socks.Errors[7]
|
||||
}
|
||||
err = socks.Errors[9]
|
||||
default:
|
||||
return nil, socks.Errors[7]
|
||||
}
|
||||
|
||||
return addr, err // skip VER, CMD, RSV fields
|
||||
}
|
||||
|
86
proxy/ss/dialer.go
Normal file
86
proxy/ss/dialer.go
Normal file
@ -0,0 +1,86 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ss", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*SS
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewSS(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{SS: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
target := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ss] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
if network == "uot" {
|
||||
target[0] = target[0] | 0x8
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
if _, err = c.Write(target); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, err
|
||||
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dialudp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(s.PacketConn(pc), nextHop, socks.ParseAddr(addr), true)
|
||||
return pkc, nextHop, err
|
||||
}
|
194
proxy/ss/server.go
Normal file
194
proxy/ss/server.go
Normal file
@ -0,0 +1,194 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("ss", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*SS
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewSS(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{SS: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP serves tcp requests
|
||||
func (s *Server) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[ss] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[ss] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP serves tcp requests
|
||||
func (s *Server) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
|
||||
tgt, err := socks.ReadAddr(c)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dialer := s.NextDialer(tgt.String())
|
||||
|
||||
// udp over tcp?
|
||||
uot := socks.UoT(tgt[0])
|
||||
if uot && dialer.Addr() == "DIRECT" {
|
||||
rc, err := net.ListenPacket("udp", "")
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] UDP remote listen error: %v", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
req := make([]byte, conn.UDPBufSize)
|
||||
n, err := c.Read(req)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] error in ioutil.ReadAll: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String())
|
||||
rc.WriteTo(req[:n], tgtAddr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
n, _, err = rc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] read error: %v", err)
|
||||
}
|
||||
|
||||
c.Write(buf[:n])
|
||||
|
||||
log.F("[ss] %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
if uot {
|
||||
network = "udp"
|
||||
}
|
||||
|
||||
rc, err := dialer.Dial(network, tgt.String())
|
||||
if err != nil {
|
||||
log.F("[ss] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[ss] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[ss] relay error: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp requests
|
||||
func (s *Server) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
lc = s.PacketConn(lc)
|
||||
|
||||
log.F("[ss-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
lpc, nextHop, err := s.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[ss-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
}
|
236
proxy/ss/ss.go
236
proxy/ss/ss.go
@ -1,36 +1,22 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// SS .
|
||||
type SS struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
addr string
|
||||
core.Cipher
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ss", NewSSDialer)
|
||||
proxy.RegisterServer("ss", NewSSServer)
|
||||
}
|
||||
|
||||
// NewSS returns a shadowsocks proxy.
|
||||
func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
||||
func NewSS(s string) (*SS, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -47,7 +33,6 @@ func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
||||
}
|
||||
|
||||
p := &SS{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
Cipher: ciph,
|
||||
}
|
||||
@ -55,224 +40,7 @@ func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewSSDialer returns a ss proxy dialer.
|
||||
func NewSSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewSS(s, dialer)
|
||||
}
|
||||
|
||||
// NewSSServer returns a ss proxy server.
|
||||
func NewSSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewSS(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe serves ss requests.
|
||||
func (s *SS) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP serves tcp ss requests.
|
||||
func (s *SS) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[ss] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[ss] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP serves tcp ss requests.
|
||||
func (s *SS) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
|
||||
tgt, err := socks.ReadAddr(c)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dialer := s.dialer.NextDialer(tgt.String())
|
||||
|
||||
// udp over tcp?
|
||||
uot := socks.UoT(tgt[0])
|
||||
if uot && dialer.Addr() == "DIRECT" {
|
||||
rc, err := net.ListenPacket("udp", "")
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] UDP remote listen error: %v", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
req := make([]byte, conn.UDPBufSize)
|
||||
n, err := c.Read(req)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] error in ioutil.ReadAll: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String())
|
||||
rc.WriteTo(req[:n], tgtAddr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
n, _, err = rc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] read error: %v", err)
|
||||
}
|
||||
|
||||
c.Write(buf[:n])
|
||||
|
||||
log.F("[ss] %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
if uot {
|
||||
network = "udp"
|
||||
}
|
||||
|
||||
rc, err := dialer.Dial(network, tgt.String())
|
||||
if err != nil {
|
||||
log.F("[ss] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[ss] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[ss] relay error: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp ss requests.
|
||||
func (s *SS) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
lc = s.PacketConn(lc)
|
||||
|
||||
log.F("[ss-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
lpc, nextHop, err := s.dialer.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[ss-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// ListCipher .
|
||||
func ListCipher() string {
|
||||
return strings.Join(core.ListCipher(), " ")
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *SS) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *SS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *SS) Dial(network, addr string) (net.Conn, error) {
|
||||
target := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ss] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
if network == "uot" {
|
||||
target[0] = target[0] | 0x8
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
if _, err = c.Write(target); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, err
|
||||
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dialudp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(s.PacketConn(pc), nextHop, socks.ParseAddr(addr), true)
|
||||
return pkc, nextHop, err
|
||||
}
|
||||
|
130
proxy/ssr/dialer.go
Normal file
130
proxy/ssr/dialer.go
Normal file
@ -0,0 +1,130 @@
|
||||
package ssr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
shadowsocksr "github.com/sun8911879/shadowsocksR"
|
||||
"github.com/sun8911879/shadowsocksR/obfs"
|
||||
"github.com/sun8911879/shadowsocksR/protocol"
|
||||
"github.com/sun8911879/shadowsocksR/ssr"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ssr", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*SSR
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewSSR(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{SSR: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
target := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ssr] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
cipher, err := shadowsocksr.NewStreamCipher(s.EncryptMethod, s.EncryptPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ssr] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ssrconn := shadowsocksr.NewSSTCPConn(c, cipher)
|
||||
if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil {
|
||||
return nil, errors.New("[ssr] nil connection")
|
||||
}
|
||||
|
||||
// should initialize obfs/protocol now
|
||||
rs := strings.Split(ssrconn.RemoteAddr().String(), ":")
|
||||
port, _ := strconv.Atoi(rs[1])
|
||||
|
||||
ssrconn.IObfs = obfs.NewObfs(s.Obfs)
|
||||
if ssrconn.IObfs == nil {
|
||||
return nil, errors.New("[ssr] unsupported obfs type: " + s.Obfs)
|
||||
}
|
||||
|
||||
obfsServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ObfsParam,
|
||||
}
|
||||
ssrconn.IObfs.SetServerInfo(obfsServerInfo)
|
||||
|
||||
ssrconn.IProtocol = protocol.NewProtocol(s.Protocol)
|
||||
if ssrconn.IProtocol == nil {
|
||||
return nil, errors.New("[ssr] unsupported protocol type: " + s.Protocol)
|
||||
}
|
||||
|
||||
protocolServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ProtocolParam,
|
||||
}
|
||||
ssrconn.IProtocol.SetServerInfo(protocolServerInfo)
|
||||
|
||||
if s.ObfsData == nil {
|
||||
s.ObfsData = ssrconn.IObfs.GetData()
|
||||
}
|
||||
ssrconn.IObfs.SetData(s.ObfsData)
|
||||
|
||||
if s.ProtocolData == nil {
|
||||
s.ProtocolData = ssrconn.IProtocol.GetData()
|
||||
}
|
||||
ssrconn.IProtocol.SetData(s.ProtocolData)
|
||||
|
||||
if _, err := ssrconn.Write(target); err != nil {
|
||||
ssrconn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ssrconn, err
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("[ssr] udp not supported now")
|
||||
}
|
113
proxy/ssr/ssr.go
113
proxy/ssr/ssr.go
@ -1,26 +1,14 @@
|
||||
package ssr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
shadowsocksr "github.com/sun8911879/shadowsocksR"
|
||||
"github.com/sun8911879/shadowsocksR/obfs"
|
||||
"github.com/sun8911879/shadowsocksR/protocol"
|
||||
"github.com/sun8911879/shadowsocksR/ssr"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// SSR .
|
||||
type SSR struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
addr string
|
||||
|
||||
EncryptMethod string
|
||||
EncryptPassword string
|
||||
@ -32,12 +20,8 @@ type SSR struct {
|
||||
ProtocolData interface{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ssr", NewSSRDialer)
|
||||
}
|
||||
|
||||
// NewSSR returns a shadowsocksr proxy, ssr://method:pass@host:port/query
|
||||
func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) {
|
||||
func NewSSR(s string) (*SSR, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -49,7 +33,6 @@ func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) {
|
||||
pass, _ := u.User.Password()
|
||||
|
||||
p := &SSR{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
EncryptMethod: method,
|
||||
EncryptPassword: pass,
|
||||
@ -63,95 +46,3 @@ func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) {
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewSSRDialer returns a ssr proxy dialer.
|
||||
func NewSSRDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewSSR(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *SSR) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *SSR) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *SSR) Dial(network, addr string) (net.Conn, error) {
|
||||
target := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ssr] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
cipher, err := shadowsocksr.NewStreamCipher(s.EncryptMethod, s.EncryptPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ssr] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ssrconn := shadowsocksr.NewSSTCPConn(c, cipher)
|
||||
if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil {
|
||||
return nil, errors.New("[ssr] nil connection")
|
||||
}
|
||||
|
||||
// should initialize obfs/protocol now
|
||||
rs := strings.Split(ssrconn.RemoteAddr().String(), ":")
|
||||
port, _ := strconv.Atoi(rs[1])
|
||||
|
||||
ssrconn.IObfs = obfs.NewObfs(s.Obfs)
|
||||
if ssrconn.IObfs == nil {
|
||||
return nil, errors.New("[ssr] unsupported obfs type: " + s.Obfs)
|
||||
}
|
||||
|
||||
obfsServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ObfsParam,
|
||||
}
|
||||
ssrconn.IObfs.SetServerInfo(obfsServerInfo)
|
||||
|
||||
ssrconn.IProtocol = protocol.NewProtocol(s.Protocol)
|
||||
if ssrconn.IProtocol == nil {
|
||||
return nil, errors.New("[ssr] unsupported protocol type: " + s.Protocol)
|
||||
}
|
||||
|
||||
protocolServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ProtocolParam,
|
||||
}
|
||||
ssrconn.IProtocol.SetServerInfo(protocolServerInfo)
|
||||
|
||||
if s.ObfsData == nil {
|
||||
s.ObfsData = ssrconn.IObfs.GetData()
|
||||
}
|
||||
ssrconn.IObfs.SetData(s.ObfsData)
|
||||
|
||||
if s.ProtocolData == nil {
|
||||
s.ProtocolData = ssrconn.IProtocol.GetData()
|
||||
}
|
||||
ssrconn.IProtocol.SetData(s.ProtocolData)
|
||||
|
||||
if _, err := ssrconn.Write(target); err != nil {
|
||||
ssrconn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ssrconn, err
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *SSR) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("[ssr] udp not supported now")
|
||||
}
|
||||
|
80
proxy/tcptun/server.go
Normal file
80
proxy/tcptun/server.go
Normal file
@ -0,0 +1,80 @@
|
||||
package tcptun
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("tcptun", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*TCPTun
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewTCPTun(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{TCPTun: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
rc, err := s.Dial("tcp", s.raddr)
|
||||
if err != nil {
|
||||
|
||||
log.F("failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[tcptun] %s <-> %s", c.RemoteAddr(), s.raddr)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("relay error: %v", err)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
}
|
@ -1,29 +1,20 @@
|
||||
package tcptun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// TCPTun struct
|
||||
type TCPTun struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
addr string
|
||||
raddr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("tcptun", NewTCPTunServer)
|
||||
}
|
||||
|
||||
// NewTCPTun returns a tcptun proxy.
|
||||
func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) {
|
||||
func NewTCPTun(s string) (*TCPTun, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -34,61 +25,9 @@ func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) {
|
||||
d := strings.Split(addr, "=")
|
||||
|
||||
p := &TCPTun{
|
||||
dialer: dialer,
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewTCPTunServer returns a udp tunnel server.
|
||||
func NewTCPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewTCPTun(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *TCPTun) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("tcp", s.raddr)
|
||||
if err != nil {
|
||||
|
||||
log.F("failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[tcptun] %s <-> %s", c.RemoteAddr(), s.raddr)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("relay error: %v", err)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
65
proxy/tls/dialer.go
Normal file
65
proxy/tls/dialer.go
Normal file
@ -0,0 +1,65 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
stdtls "crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("tls", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*TLS
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewTLS(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{TLS: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string { return s.addr }
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
cc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[tls] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := &stdtls.Config{
|
||||
ServerName: s.serverName,
|
||||
InsecureSkipVerify: s.skipVerify,
|
||||
}
|
||||
|
||||
c := stdtls.Client(cc, conf)
|
||||
err = c.Handshake()
|
||||
return c, err
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("tls client does not support udp now")
|
||||
}
|
@ -1,31 +1,22 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
stdtls "crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// TLS .
|
||||
type TLS struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
addr string
|
||||
|
||||
serverName string
|
||||
skipVerify bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("tls", NewTLSDialer)
|
||||
}
|
||||
|
||||
// NewTLS returns a tls proxy.
|
||||
func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
||||
func NewTLS(s string) (*TLS, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse url err: %s", err)
|
||||
@ -44,7 +35,6 @@ func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
||||
serverName := addr[:colonPos]
|
||||
|
||||
p := &TLS{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
serverName: serverName,
|
||||
skipVerify: false,
|
||||
@ -56,37 +46,3 @@ func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewTLSDialer returns a tls proxy dialer.
|
||||
func NewTLSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewTLS(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *TLS) Addr() string { return s.addr }
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *TLS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *TLS) Dial(network, addr string) (net.Conn, error) {
|
||||
cc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[tls] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := &stdtls.Config{
|
||||
ServerName: s.serverName,
|
||||
InsecureSkipVerify: s.skipVerify,
|
||||
}
|
||||
|
||||
c := stdtls.Client(cc, conf)
|
||||
err = c.Handshake()
|
||||
return c, err
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *TLS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("tls client does not support udp now")
|
||||
}
|
||||
|
@ -17,18 +17,18 @@ import (
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// TProxy struct
|
||||
type TProxy struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("tproxy", NewTProxyServer)
|
||||
proxy.RegisterServer("tproxy", CreateServer)
|
||||
}
|
||||
|
||||
// NewTProxy returns a tproxy.
|
||||
func NewTProxy(s string, dialer proxy.Dialer) (*TProxy, error) {
|
||||
// Server struct
|
||||
type Server struct {
|
||||
addr string
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -37,32 +37,32 @@ func NewTProxy(s string, dialer proxy.Dialer) (*TProxy, error) {
|
||||
|
||||
addr := u.Host
|
||||
|
||||
p := &TProxy{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
p := &Server{
|
||||
addr: addr,
|
||||
Forwarder: f,
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewTProxyServer returns a udp tunnel server.
|
||||
func NewTProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewTProxy(s, dialer)
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *TProxy) ListenAndServe() {
|
||||
func (s *Server) ListenAndServe() {
|
||||
// go s.ListenAndServeTCP()
|
||||
s.ListenAndServeUDP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP .
|
||||
func (s *TProxy) ListenAndServeTCP() {
|
||||
func (s *Server) ListenAndServeTCP() {
|
||||
log.F("[tproxy] tcp mode not supported now, please use 'redir' instead")
|
||||
}
|
||||
|
||||
// ListenAndServeUDP .
|
||||
func (s *TProxy) ListenAndServeUDP() {
|
||||
func (s *Server) ListenAndServeUDP() {
|
||||
laddr, err := net.ResolveUDPAddr("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[tproxy] failed to resolve addr %s: %v", s.addr, err)
|
92
proxy/udptun/server.go
Normal file
92
proxy/udptun/server.go
Normal file
@ -0,0 +1,92 @@
|
||||
package udptun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("udptun", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*UDPTun
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewUDPTun(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{UDPTun: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
c, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[udptun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[udptun] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[udptun] read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc net.PacketConn
|
||||
var writeAddr net.Addr
|
||||
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
|
||||
pc, writeAddr, err = s.DialUDP("udp", s.raddr)
|
||||
if err != nil {
|
||||
log.F("[udptun] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(net.PacketConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], writeAddr)
|
||||
if err != nil {
|
||||
log.F("[udptun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[udptun] %s <-> %s", raddr, s.raddr)
|
||||
|
||||
}
|
||||
}
|
@ -1,31 +1,20 @@
|
||||
package udptun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// UDPTun struct
|
||||
type UDPTun struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
addr string
|
||||
raddr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("udptun", NewUDPTunServer)
|
||||
}
|
||||
|
||||
// NewUDPTun returns a UDPTun proxy.
|
||||
func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) {
|
||||
func NewUDPTun(s string) (*UDPTun, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -36,71 +25,9 @@ func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) {
|
||||
d := strings.Split(addr, "=")
|
||||
|
||||
p := &UDPTun{
|
||||
dialer: dialer,
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewUDPTunServer returns a udp tunnel server.
|
||||
func NewUDPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewUDPTun(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *UDPTun) ListenAndServe() {
|
||||
c, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[udptun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[udptun] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[udptun] read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc net.PacketConn
|
||||
var writeAddr net.Addr
|
||||
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
|
||||
pc, writeAddr, err = s.dialer.DialUDP("udp", s.raddr)
|
||||
if err != nil {
|
||||
log.F("[udptun] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(net.PacketConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], writeAddr)
|
||||
if err != nil {
|
||||
log.F("[udptun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[udptun] %s <-> %s", raddr, s.raddr)
|
||||
|
||||
}
|
||||
}
|
||||
|
90
proxy/uottun/server.go
Normal file
90
proxy/uottun/server.go
Normal file
@ -0,0 +1,90 @@
|
||||
package uottun
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("uottun", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*UoTTun
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewUoTTun(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{UoTTun: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
c, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[uottun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[uottun] listening UDP on %s", s.addr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
n, clientAddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[uottun] read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rc, err := s.Dial("uot", s.raddr)
|
||||
if err != nil {
|
||||
log.F("[uottun] failed to connect to server %v: %v", s.raddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
go func() {
|
||||
// no remote forwarder, just a local udp forwarder
|
||||
if urc, ok := rc.(*net.UDPConn); ok {
|
||||
conn.TimedCopy(c, clientAddr, urc, 2*time.Minute)
|
||||
urc.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remote forwarder, udp over tcp
|
||||
resp, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
log.F("error in ioutil.ReadAll: %s\n", err)
|
||||
return
|
||||
}
|
||||
rc.Close()
|
||||
c.WriteTo(resp, clientAddr)
|
||||
}()
|
||||
|
||||
_, err = rc.Write(buf[:n])
|
||||
if err != nil {
|
||||
log.F("[uottun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[uottun] %s <-> %s", clientAddr, s.raddr)
|
||||
}
|
||||
}
|
@ -1,31 +1,20 @@
|
||||
package uottun
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// UoTTun udp over tcp tunnel
|
||||
type UoTTun struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
addr string
|
||||
raddr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("uottun", NewUoTTunServer)
|
||||
}
|
||||
|
||||
// NewUoTTun returns a UoTTun proxy.
|
||||
func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
||||
func NewUoTTun(s string) (*UoTTun, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -36,69 +25,9 @@ func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
||||
d := strings.Split(addr, "=")
|
||||
|
||||
p := &UoTTun{
|
||||
dialer: dialer,
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewUoTTunServer returns a uot tunnel server.
|
||||
func NewUoTTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewUoTTun(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *UoTTun) ListenAndServe() {
|
||||
c, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[uottun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[uottun] listening UDP on %s", s.addr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
n, clientAddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[uottun] read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("uot", s.raddr)
|
||||
if err != nil {
|
||||
log.F("[uottun] failed to connect to server %v: %v", s.raddr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
go func() {
|
||||
// no remote forwarder, just a local udp forwarder
|
||||
if urc, ok := rc.(*net.UDPConn); ok {
|
||||
conn.TimedCopy(c, clientAddr, urc, 2*time.Minute)
|
||||
urc.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// remote forwarder, udp over tcp
|
||||
resp, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
log.F("error in ioutil.ReadAll: %s\n", err)
|
||||
return
|
||||
}
|
||||
rc.Close()
|
||||
c.WriteTo(resp, clientAddr)
|
||||
}()
|
||||
|
||||
_, err = rc.Write(buf[:n])
|
||||
if err != nil {
|
||||
log.F("[uottun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[uottun] %s <-> %s", clientAddr, s.raddr)
|
||||
}
|
||||
}
|
||||
|
60
proxy/vmess/dialer.go
Normal file
60
proxy/vmess/dialer.go
Normal file
@ -0,0 +1,60 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("vmess", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*VMess
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewVMess(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{VMess: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("vmess client does not support udp now")
|
||||
}
|
@ -1,19 +1,15 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// VMess .
|
||||
type VMess struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
addr string
|
||||
|
||||
uuid string
|
||||
alterID int
|
||||
@ -22,12 +18,8 @@ type VMess struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("vmess", NewVMessDialer)
|
||||
}
|
||||
|
||||
// NewVMess returns a vmess proxy.
|
||||
func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) {
|
||||
func NewVMess(s string) (*VMess, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse url err: %s", err)
|
||||
@ -62,7 +54,6 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) {
|
||||
}
|
||||
|
||||
p := &VMess{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
uuid: uuid,
|
||||
alterID: int(alterID),
|
||||
@ -72,34 +63,3 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) {
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewVMessDialer returns a vmess proxy dialer.
|
||||
func NewVMessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewVMess(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *VMess) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *VMess) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *VMess) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *VMess) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("vmess client does not support udp now")
|
||||
}
|
||||
|
60
proxy/ws/dialer.go
Normal file
60
proxy/ws/dialer.go
Normal file
@ -0,0 +1,60 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ws", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*WS
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewWS(s, dialer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{WS: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("ws client does not support udp now")
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
@ -12,16 +10,10 @@ import (
|
||||
|
||||
// WS .
|
||||
type WS struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ws", NewWSDialer)
|
||||
}
|
||||
|
||||
// NewWS returns a websocket proxy.
|
||||
func NewWS(s string, dialer proxy.Dialer) (*WS, error) {
|
||||
u, err := url.Parse(s)
|
||||
@ -50,41 +42,9 @@ func NewWS(s string, dialer proxy.Dialer) (*WS, error) {
|
||||
}
|
||||
|
||||
p := &WS{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
client: client,
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewWSDialer returns a ws proxy dialer.
|
||||
func NewWSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewWS(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *WS) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *WS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *WS) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *WS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("ws client does not support udp now")
|
||||
}
|
||||
|
16
rule.go
16
rule.go
@ -23,20 +23,7 @@ func NewRuleDialer(rules []*RuleConf, gDialer proxy.Dialer) *RuleDialer {
|
||||
rd := &RuleDialer{gDialer: gDialer}
|
||||
|
||||
for _, r := range rules {
|
||||
var fwdrs []proxy.Dialer
|
||||
for _, chain := range r.Forward {
|
||||
var fwdr proxy.Dialer
|
||||
var err error
|
||||
for _, url := range strings.Split(chain, ",") {
|
||||
fwdr, err = proxy.DialerFromURL(url, fwdr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
fwdrs = append(fwdrs, fwdr)
|
||||
}
|
||||
|
||||
sDialer := NewStrategyDialer(r.Strategy, fwdrs, r.CheckWebSite, r.CheckInterval)
|
||||
sDialer := StrategyDialer(r.Forward, &r.StrategyConfig)
|
||||
|
||||
for _, domain := range r.Domain {
|
||||
rd.domainMap.Store(strings.ToLower(domain), sDialer)
|
||||
@ -51,7 +38,6 @@ func NewRuleDialer(rules []*RuleConf, gDialer proxy.Dialer) *RuleDialer {
|
||||
rd.cidrMap.Store(cidr, sDialer)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rd
|
||||
|
69
strategy.go
69
strategy.go
@ -12,27 +12,44 @@ import (
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// NewStrategyDialer returns a new Strategy proxy.Dialer
|
||||
func NewStrategyDialer(strategy string, dialers []proxy.Dialer, website string, interval int) proxy.Dialer {
|
||||
if len(dialers) == 0 {
|
||||
// StrategyConfig .
|
||||
type StrategyConfig struct {
|
||||
Strategy string
|
||||
CheckWebSite string
|
||||
CheckInterval int
|
||||
}
|
||||
|
||||
// StrategyDialer .
|
||||
func StrategyDialer(s []string, c *StrategyConfig) proxy.Dialer {
|
||||
// global forwarders in xx.conf
|
||||
var fwdrs []*proxy.Forwarder
|
||||
for _, chain := range s {
|
||||
fwdr, err := proxy.ForwarderFromURL(chain)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fwdrs = append(fwdrs, fwdr)
|
||||
}
|
||||
|
||||
if len(fwdrs) == 0 {
|
||||
return proxy.Direct
|
||||
}
|
||||
|
||||
if len(dialers) == 1 {
|
||||
return dialers[0]
|
||||
if len(fwdrs) == 1 {
|
||||
return fwdrs[0]
|
||||
}
|
||||
|
||||
var dialer proxy.Dialer
|
||||
switch strategy {
|
||||
switch c.Strategy {
|
||||
case "rr":
|
||||
dialer = newRRDialer(dialers, website, interval)
|
||||
dialer = newRRDialer(fwdrs, c.CheckWebSite, c.CheckInterval)
|
||||
log.F("forward to remote servers in round robin mode.")
|
||||
case "ha":
|
||||
dialer = newHADialer(dialers, website, interval)
|
||||
dialer = newHADialer(fwdrs, c.CheckWebSite, c.CheckInterval)
|
||||
log.F("forward to remote servers in high availability mode.")
|
||||
default:
|
||||
log.F("not supported forward mode '%s', just use the first forward server.", conf.Strategy)
|
||||
dialer = dialers[0]
|
||||
log.F("not supported forward mode '%s', just use the first forward server.", c.Strategy)
|
||||
dialer = fwdrs[0]
|
||||
}
|
||||
|
||||
return dialer
|
||||
@ -40,8 +57,8 @@ func NewStrategyDialer(strategy string, dialers []proxy.Dialer, website string,
|
||||
|
||||
// rrDialer is the base struct of strategy dialer
|
||||
type rrDialer struct {
|
||||
dialers []proxy.Dialer
|
||||
idx int
|
||||
fwdrs []*proxy.Forwarder
|
||||
idx int
|
||||
|
||||
status sync.Map
|
||||
|
||||
@ -51,13 +68,13 @@ type rrDialer struct {
|
||||
}
|
||||
|
||||
// newRRDialer returns a new rrDialer
|
||||
func newRRDialer(dialers []proxy.Dialer, website string, interval int) *rrDialer {
|
||||
rr := &rrDialer{dialers: dialers}
|
||||
func newRRDialer(fwdrs []*proxy.Forwarder, website string, interval int) *rrDialer {
|
||||
rr := &rrDialer{fwdrs: fwdrs}
|
||||
|
||||
rr.website = website
|
||||
rr.interval = interval
|
||||
|
||||
for k := range dialers {
|
||||
for k := range fwdrs {
|
||||
rr.status.Store(k, true)
|
||||
go rr.checkDialer(k)
|
||||
}
|
||||
@ -74,8 +91,8 @@ func (rr *rrDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo ne
|
||||
return rr.NextDialer(addr).DialUDP(network, addr)
|
||||
}
|
||||
|
||||
func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer {
|
||||
n := len(rr.dialers)
|
||||
func (rr *rrDialer) nextDialer(dstAddr string) *proxy.Forwarder {
|
||||
n := len(rr.fwdrs)
|
||||
if n == 1 {
|
||||
rr.idx = 0
|
||||
}
|
||||
@ -94,7 +111,11 @@ func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer {
|
||||
log.F("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.")
|
||||
}
|
||||
|
||||
return rr.dialers[rr.idx]
|
||||
return rr.fwdrs[rr.idx]
|
||||
}
|
||||
|
||||
func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer {
|
||||
return rr.nextDialer(dstAddr)
|
||||
}
|
||||
|
||||
// Check dialer
|
||||
@ -106,7 +127,7 @@ func (rr *rrDialer) checkDialer(idx int) {
|
||||
rr.website = rr.website + ":80"
|
||||
}
|
||||
|
||||
d := rr.dialers[idx]
|
||||
d := rr.fwdrs[idx]
|
||||
|
||||
for {
|
||||
time.Sleep(time.Duration(rr.interval) * time.Second * time.Duration(retry>>1))
|
||||
@ -150,27 +171,27 @@ type haDialer struct {
|
||||
}
|
||||
|
||||
// newHADialer .
|
||||
func newHADialer(dialers []proxy.Dialer, webhost string, duration int) proxy.Dialer {
|
||||
func newHADialer(dialers []*proxy.Forwarder, webhost string, duration int) proxy.Dialer {
|
||||
return &haDialer{rrDialer: newRRDialer(dialers, webhost, duration)}
|
||||
}
|
||||
|
||||
func (ha *haDialer) Dial(network, addr string) (net.Conn, error) {
|
||||
d := ha.dialers[ha.idx]
|
||||
d := ha.fwdrs[ha.idx]
|
||||
|
||||
result, ok := ha.status.Load(ha.idx)
|
||||
if ok && !result.(bool) {
|
||||
d = ha.NextDialer(addr)
|
||||
d = ha.nextDialer(addr)
|
||||
}
|
||||
|
||||
return d.Dial(network, addr)
|
||||
}
|
||||
|
||||
func (ha *haDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
d := ha.dialers[ha.idx]
|
||||
d := ha.fwdrs[ha.idx]
|
||||
|
||||
result, ok := ha.status.Load(ha.idx)
|
||||
if ok && !result.(bool) {
|
||||
d = ha.NextDialer(addr)
|
||||
d = ha.nextDialer(addr)
|
||||
}
|
||||
|
||||
return d.DialUDP(network, addr)
|
||||
|
Loading…
Reference in New Issue
Block a user