general: add a forwarder struct and changed proxies to use it

This commit is contained in:
nadoo 2018-08-10 19:03:30 +08:00
parent 2fcef7b00c
commit 0da05ecedd
34 changed files with 1806 additions and 1474 deletions

37
conf.go
View File

@ -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
View File

@ -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
View 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
View 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")
}

View File

@ -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
View 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)
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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
View 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
View 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
}

View File

@ -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
View 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
View 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)
}
}

View File

@ -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
View 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")
}

View File

@ -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
View 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)
}
}()
}
}

View File

@ -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
View 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")
}

View File

@ -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")
}

View File

@ -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
View 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)
}
}

View File

@ -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
View 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)
}
}

View File

@ -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
View 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")
}

View File

@ -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
View 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")
}

View File

@ -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
View File

@ -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

View File

@ -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)