proxy: added the ability to log forwarder

This commit is contained in:
nadoo 2019-09-18 12:53:04 +08:00
parent c2425e67de
commit 063dc1bc01
22 changed files with 138 additions and 141 deletions

View File

@ -12,11 +12,6 @@ jobs:
- name: Check out code
uses: actions/checkout@v1
- name: Validates GO releaser config
uses: docker://goreleaser/goreleaser:latest
with:
args: check
- name: Create release on GitHub
uses: docker://goreleaser/goreleaser:latest
env:

View File

@ -1,9 +1,7 @@
# [glider](https://github.com/nadoo/glider)
[![Build Status](https://img.shields.io/travis/nadoo/glider.svg?style=flat-square)](https://travis-ci.org/nadoo/glider)
[![Go Report Card](https://goreportcard.com/badge/github.com/nadoo/glider?style=flat-square)](https://goreportcard.com/report/github.com/nadoo/glider)
[![GitHub tag](https://img.shields.io/github/tag/nadoo/glider.svg?style=flat-square)](https://github.com/nadoo/glider/releases)
[![GitHub release](https://img.shields.io/github/release/nadoo/glider.svg?style=flat-square)](https://github.com/nadoo/glider/releases)
[![GitHub release](https://img.shields.io/github/v/release/nadoo/glider.svg?include_prereleases&style=flat-square)](https://github.com/nadoo/glider/releases)
glider is a forward proxy with multiple protocols support, and also a dns forwarding server with ipset management features(like dnsmasq).

View File

@ -143,7 +143,7 @@ func (c *Client) exchange(qname string, reqBytes []byte, preferTCP bool) (server
servers := c.GetServers(qname)
for _, server = range servers {
var rc net.Conn
rc, err = dialer.Dial(network, server)
rc, _, err = dialer.Dial(network, server)
if err != nil {
log.F("[dns] failed to connect to server %v: %v", server, err)
continue

View File

@ -15,7 +15,7 @@ type Dialer interface {
Addr() string
// Dial connects to the given address via the proxy.
Dial(network, addr string) (c net.Conn, err error)
Dial(network, addr string) (c net.Conn, proxy string, err error)
// DialUDP connects to the given address via the proxy.
DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error)

View File

@ -39,7 +39,7 @@ func NewDirect(intface string) (*Direct, error) {
func (d *Direct) Addr() string { return "DIRECT" }
// Dial connects to the address addr on the network net
func (d *Direct) Dial(network, addr string) (c net.Conn, err error) {
func (d *Direct) Dial(network, addr string) (c net.Conn, p string, err error) {
if d.iface == nil || d.ip != nil {
c, err = dial(network, addr, d.ip)
if err == nil {
@ -60,7 +60,7 @@ func (d *Direct) Dial(network, addr string) (c net.Conn, err error) {
err = errors.New("dial failed, maybe the interface link is down, please check it")
}
return
return c, "DIRECT", err
}
func dial(network, addr string, localIP net.IP) (net.Conn, error) {

View File

@ -142,10 +142,10 @@ func (s *HTTP) Serve(c net.Conn) {
tgt += ":80"
}
rc, err := s.dialer.Dial("tcp", tgt)
rc, p, err := s.dialer.Dial("tcp", tgt)
if err != nil {
fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
log.F("[http] %s <-> %s, error in dial: %v", c.RemoteAddr(), tgt, err)
log.F("[http] %s <-> %s, %s, error in dial: %v", c.RemoteAddr(), tgt, err, p)
return
}
defer rc.Close()
@ -192,7 +192,7 @@ func (s *HTTP) Serve(c net.Conn) {
writeFirstLine(&respBuf, proto, code, status)
writeHeaders(&respBuf, respHeader)
log.F("[http] %s <-> %s", c.RemoteAddr(), tgt)
log.F("[http] %s <-> %s, %s", c.RemoteAddr(), tgt, p)
c.Write(respBuf.Bytes())
io.Copy(c, respR)
@ -200,7 +200,7 @@ func (s *HTTP) Serve(c net.Conn) {
}
func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) {
rc, err := s.dialer.Dial("tcp", requestURI)
rc, p, err := s.dialer.Dial("tcp", requestURI)
if err != nil {
c.Write([]byte(proto))
c.Write([]byte(" 502 ERROR\r\n\r\n"))
@ -210,7 +210,7 @@ func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) {
c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
log.F("[http] %s <-> %s [c]", c.RemoteAddr(), requestURI)
log.F("[http] %s <-> %s [c], %s", c.RemoteAddr(), requestURI, p)
_, _, err = conn.Relay(c, rc)
if err != nil {
@ -233,11 +233,11 @@ func (s *HTTP) Addr() string {
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)
func (s *HTTP) Dial(network, addr string) (net.Conn, string, error) {
rc, p, err := s.dialer.Dial(network, s.addr)
if err != nil {
log.F("[http] dial to %s error: %s", s.addr, err)
return nil, err
return nil, p, err
}
var buf bytes.Buffer
@ -254,7 +254,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
buf.Write([]byte("\r\n"))
_, err = rc.Write(buf.Bytes())
if err != nil {
return nil, err
return nil, p, err
}
c := conn.NewConn(rc)
@ -262,7 +262,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
_, code, _, ok := parseFirstLine(tpr)
if ok && code == "200" {
tpr.ReadMIMEHeader()
return c, err
return c, p, err
}
if code == "407" {
@ -271,7 +271,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
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)
return nil, p, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code)
}
// DialUDP connects to the given address via the proxy
@ -282,9 +282,7 @@ func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Add
// 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)
return
}

View File

@ -197,12 +197,12 @@ func (s *KCP) Addr() string { return s.addr }
func (s *KCP) 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 *KCP) Dial(network, addr string) (net.Conn, error) {
func (s *KCP) Dial(network, addr string) (net.Conn, string, error) {
// NOTE: kcp uses udp, we should dial remote server directly here
c, err := kcp.DialWithOptions(s.addr, s.block, s.dataShards, s.parityShards)
if err != nil {
log.F("[tls] dial to %s error: %s", s.addr, err)
return nil, err
return nil, "", err
}
// TODO: change them to customizable later?
@ -217,7 +217,7 @@ func (s *KCP) Dial(network, addr string) (net.Conn, error) {
c.SetReadBuffer(4194304)
c.SetWriteBuffer(4194304)
return c, err
return c, "", err
}
// DialUDP connects to the given address via the proxy

View File

@ -93,14 +93,15 @@ func (s *Obfs) Addr() string { return s.addr }
func (s *Obfs) 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 *Obfs) Dial(network, addr string) (net.Conn, error) {
c, err := s.dialer.Dial("tcp", s.addr)
func (s *Obfs) Dial(network, addr string) (net.Conn, string, error) {
c, p, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
log.F("[obfs] dial to %s error: %s", s.addr, err)
return nil, err
return nil, p, err
}
return s.obfsConn(c)
cc, e := s.obfsConn(c)
return cc, p, e
}
// DialUDP connects to the given address via the proxy

View File

@ -104,14 +104,14 @@ func (s *RedirProxy) Serve(c net.Conn) {
return
}
rc, err := s.dialer.Dial("tcp", tgt.String())
rc, p, err := s.dialer.Dial("tcp", tgt.String())
if err != nil {
log.F("[redir] %s <-> %s, error in dial: %v", c.RemoteAddr(), tgt, err)
log.F("[redir] %s <-> %s, %s, error in dial: %v", c.RemoteAddr(), tgt, err, p)
return
}
defer rc.Close()
log.F("[redir] %s <-> %s", c.RemoteAddr(), tgt)
log.F("[redir] %s <-> %s, %s", c.RemoteAddr(), tgt, p)
_, _, err = conn.Relay(c, rc)
if err != nil {

View File

@ -32,7 +32,9 @@ func (s *Reject) Addr() string { return "REJECT" }
func (s *Reject) NextDialer(dstAddr string) proxy.Dialer { return s }
// Dial connects to the address addr on the network net via the proxy.
func (s *Reject) Dial(network, addr string) (net.Conn, error) { return nil, errors.New("REJECT") }
func (s *Reject) Dial(network, addr string) (net.Conn, string, error) {
return nil, "REJECT", errors.New("REJECT")
}
// DialUDP connects to the given address via the proxy.
func (s *Reject) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {

View File

@ -131,14 +131,14 @@ func (s *SOCKS5) Serve(c net.Conn) {
return
}
rc, err := s.dialer.Dial("tcp", tgt.String())
rc, p, err := s.dialer.Dial("tcp", tgt.String())
if err != nil {
log.F("[socks5] %s <-> %s, error in dial: %v", c.RemoteAddr(), tgt, err)
log.F("[socks5] %s <-> %s, %s, error in dial: %v", c.RemoteAddr(), tgt, err, p)
return
}
defer rc.Close()
log.F("[socks5] %s <-> %s", c.RemoteAddr(), tgt)
log.F("[socks5] %s <-> %s, %s", c.RemoteAddr(), tgt, p)
_, _, err = conn.Relay(c, rc)
if err != nil {
@ -224,30 +224,30 @@ func (s *SOCKS5) Addr() string {
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) {
func (s *SOCKS5) Dial(network, addr string) (net.Conn, string, error) {
switch network {
case "tcp", "tcp6", "tcp4":
default:
return nil, errors.New("[socks5]: no support for connection type " + network)
return nil, "", errors.New("[socks5]: no support for connection type " + network)
}
c, err := s.dialer.Dial(network, s.addr)
c, p, err := s.dialer.Dial(network, s.addr)
if err != nil {
log.F("[socks5]: dial to %s error: %s", s.addr, err)
return nil, err
return nil, p, err
}
if err := s.connect(c, addr); err != nil {
c.Close()
return nil, err
return nil, p, err
}
return c, nil
return c, p, 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)
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

View File

@ -147,14 +147,14 @@ func (s *SS) Serve(c net.Conn) {
network = "udp"
}
rc, err := dialer.Dial(network, tgt.String())
rc, p, err := dialer.Dial(network, tgt.String())
if err != nil {
log.F("[ss] %s <-> %s, error in dial: %v", c.RemoteAddr(), tgt, err)
log.F("[ss] %s <-> %s, %s, error in dial: %v", c.RemoteAddr(), tgt, err, p)
return
}
defer rc.Close()
log.F("[ss] %s <-> %s", c.RemoteAddr(), tgt)
log.F("[ss] %s <-> %s, %s", c.RemoteAddr(), tgt, p)
_, _, err = conn.Relay(c, rc)
if err != nil {
@ -242,29 +242,29 @@ func (s *SS) Addr() string {
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) {
func (s *SS) Dial(network, addr string) (net.Conn, string, error) {
target := socks.ParseAddr(addr)
if target == nil {
return nil, errors.New("[ss] unable to parse address: " + addr)
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)
c, p, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
log.F("[ss] dial to %s error: %s", s.addr, err)
return nil, err
return nil, p, err
}
c = s.StreamConn(c)
if _, err = c.Write(target); err != nil {
c.Close()
return nil, err
return nil, p, err
}
return c, err
return c, p, err
}

View File

@ -81,26 +81,26 @@ func (s *SSR) Addr() string {
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) {
func (s *SSR) Dial(network, addr string) (net.Conn, string, error) {
target := socks.ParseAddr(addr)
if target == nil {
return nil, errors.New("[ssr] unable to parse address: " + addr)
return nil, "", errors.New("[ssr] unable to parse address: " + addr)
}
cipher, err := shadowsocksr.NewStreamCipher(s.EncryptMethod, s.EncryptPassword)
if err != nil {
return nil, err
return nil, "", err
}
c, err := s.dialer.Dial("tcp", s.addr)
c, p, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
log.F("[ssr] dial to %s error: %s", s.addr, err)
return nil, err
return nil, p, err
}
ssrconn := shadowsocksr.NewSSTCPConn(c, cipher)
if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil {
return nil, errors.New("[ssr] nil connection")
return nil, p, errors.New("[ssr] nil connection")
}
// should initialize obfs/protocol now
@ -109,7 +109,7 @@ func (s *SSR) Dial(network, addr string) (net.Conn, error) {
ssrconn.IObfs = obfs.NewObfs(s.Obfs)
if ssrconn.IObfs == nil {
return nil, errors.New("[ssr] unsupported obfs type: " + s.Obfs)
return nil, p, errors.New("[ssr] unsupported obfs type: " + s.Obfs)
}
obfsServerInfo := &ssr.ServerInfoForObfs{
@ -122,7 +122,7 @@ func (s *SSR) Dial(network, addr string) (net.Conn, error) {
ssrconn.IProtocol = protocol.NewProtocol(s.Protocol)
if ssrconn.IProtocol == nil {
return nil, errors.New("[ssr] unsupported protocol type: " + s.Protocol)
return nil, p, errors.New("[ssr] unsupported protocol type: " + s.Protocol)
}
protocolServerInfo := &ssr.ServerInfoForObfs{
@ -145,10 +145,10 @@ func (s *SSR) Dial(network, addr string) (net.Conn, error) {
if _, err := ssrconn.Write(target); err != nil {
ssrconn.Close()
return nil, err
return nil, p, err
}
return ssrconn, err
return ssrconn, p, err
}
// DialUDP connects to the given address via the proxy.

View File

@ -76,14 +76,14 @@ func (s *TCPTun) Serve(c net.Conn) {
c.SetKeepAlive(true)
}
rc, err := s.dialer.Dial("tcp", s.raddr)
rc, p, err := s.dialer.Dial("tcp", s.raddr)
if err != nil {
log.F("[tcptun] %s <-> %s, error in dial: %v", c.RemoteAddr(), s.addr, err)
log.F("[tcptun] %s <-> %s, %s, error in dial: %v", c.RemoteAddr(), s.addr, err, p)
return
}
defer rc.Close()
log.F("[tcptun] %s <-> %s", c.RemoteAddr(), s.raddr)
log.F("[tcptun] %s <-> %s, %s", c.RemoteAddr(), s.raddr, p)
_, _, err = conn.Relay(c, rc)
if err != nil {

View File

@ -160,16 +160,16 @@ func (s *TLS) Addr() string { return s.addr }
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)
func (s *TLS) Dial(network, addr string) (net.Conn, string, error) {
cc, p, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
log.F("[tls] dial to %s error: %s", s.addr, err)
return nil, err
return nil, p, err
}
c := stdtls.Client(cc, s.tlsConfig)
err = c.Handshake()
return c, err
return c, p, err
}
// DialUDP connects to the given address via the proxy

View File

@ -113,14 +113,14 @@ func (s *Unix) Addr() string {
func (s *Unix) 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 *Unix) Dial(network, addr string) (net.Conn, error) {
func (s *Unix) Dial(network, addr string) (net.Conn, string, error) {
// NOTE: must be the first dialer in a chain
rc, err := net.Dial("unix", s.addr)
if err != nil {
return nil, err
return nil, "", err
}
return rc, err
return rc, "", err
}
// DialUDP connects to the given address via the proxy

View File

@ -69,7 +69,7 @@ func (s *UoTTun) ListenAndServe() {
continue
}
rc, err := s.dialer.Dial("uot", s.raddr)
rc, p, err := s.dialer.Dial("uot", s.raddr)
if err != nil {
log.F("[uottun] failed to connect to server %v: %v", s.raddr, err)
continue
@ -99,7 +99,7 @@ func (s *UoTTun) ListenAndServe() {
continue
}
log.F("[uottun] %s <-> %s", clientAddr, s.raddr)
log.F("[uottun] %s <-> %s, %s", clientAddr, s.raddr, p)
}
}

View File

@ -90,13 +90,14 @@ func (s *VMess) Addr() string {
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)
func (s *VMess) Dial(network, addr string) (net.Conn, string, error) {
rc, p, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
return nil, err
return nil, p, err
}
return s.client.NewConn(rc, addr)
cc, e := s.client.NewConn(rc, addr)
return cc, p, e
}
// DialUDP connects to the given address via the proxy.

View File

@ -76,13 +76,14 @@ func (s *WS) Addr() string {
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)
func (s *WS) Dial(network, addr string) (net.Conn, string, error) {
rc, p, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
return nil, err
return nil, p, err
}
return s.client.NewConn(rc, addr)
cc, e := s.client.NewConn(rc, addr)
return cc, p, e
}
// DialUDP connects to the given address via the proxy.

View File

@ -12,8 +12,8 @@ import (
// Dialer struct
type Dialer struct {
gDialer proxy.Dialer
dialers []proxy.Dialer
gDialer *strategy.Dialer
dialers []*strategy.Dialer
domainMap sync.Map
ipMap sync.Map
@ -21,7 +21,7 @@ type Dialer struct {
}
// NewDialer returns a new rule dialer
func NewDialer(rules []*Config, gDialer proxy.Dialer) *Dialer {
func NewDialer(rules []*Config, gDialer *strategy.Dialer) *Dialer {
rd := &Dialer{gDialer: gDialer}
for _, r := range rules {
@ -98,17 +98,13 @@ func (rd *Dialer) NextDialer(dstAddr string) proxy.Dialer {
}
// Dial dials to targer addr and return a conn
func (rd *Dialer) Dial(network, addr string) (net.Conn, error) {
d := rd.NextDialer(addr)
log.F("[dial] %s => %s", addr, d.Addr())
return d.Dial(network, addr)
func (rd *Dialer) Dial(network, addr string) (net.Conn, string, error) {
return rd.NextDialer(addr).Dial(network, addr)
}
// DialUDP connects to the given address via the proxy
func (rd *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
d := rd.NextDialer(addr)
log.F("[dial-udp] %s => %s", addr, d.Addr())
return d.DialUDP(network, addr)
return rd.NextDialer(addr).DialUDP(network, addr)
}
// AddDomainIP used to update ipMap rules according to domainMap rule
@ -131,13 +127,9 @@ func (rd *Dialer) AddDomainIP(domain, ip string) error {
// Check .
func (rd *Dialer) Check() {
if checker, ok := rd.gDialer.(strategy.Checker); ok {
checker.Check()
}
rd.gDialer.Check()
for _, d := range rd.dialers {
if checker, ok := d.(strategy.Checker); ok {
checker.Check()
}
d.Check()
}
}

View File

@ -1,4 +1,4 @@
package proxy
package strategy
import (
"net"
@ -8,6 +8,7 @@ import (
"sync/atomic"
"github.com/nadoo/glider/common/log"
"github.com/nadoo/glider/proxy"
)
// StatusHandler function will be called when the forwarder's status changed
@ -15,7 +16,7 @@ type StatusHandler func(*Forwarder)
// Forwarder is a forwarder
type Forwarder struct {
Dialer
proxy.Dialer
addr string
priority uint32
maxFailures uint32 // maxfailures to set to Disabled
@ -40,14 +41,14 @@ func ForwarderFromURL(s, intface string) (f *Forwarder, err error) {
iface = f.intface
}
var d Dialer
d, err = NewDirect(iface)
var d proxy.Dialer
d, err = proxy.NewDirect(iface)
if err != nil {
return nil, err
}
for _, url := range strings.Split(ss[0], ",") {
d, err = DialerFromURL(url, d)
d, err = proxy.DialerFromURL(url, d)
if err != nil {
return nil, err
}
@ -57,12 +58,21 @@ func ForwarderFromURL(s, intface string) (f *Forwarder, err error) {
f.addr = d.Addr()
// set forwarder to disabled by default
// TODO: check here
f.Disable()
return f, err
}
// DirectForwarder returns a direct forwarder
func DirectForwarder(intface string) *Forwarder {
d, err := proxy.NewDirect(intface)
if err != nil {
return nil
}
return &Forwarder{Dialer: d, addr: d.Addr()}
}
func (f *Forwarder) parseOption(option string) error {
query, err := url.ParseQuery(option)
if err != nil {
@ -87,8 +97,8 @@ func (f *Forwarder) Addr() string {
}
// Dial .
func (f *Forwarder) Dial(network, addr string) (c net.Conn, err error) {
c, err = f.Dialer.Dial(network, addr)
func (f *Forwarder) Dial(network, addr string) (c net.Conn, p string, err error) {
c, p, err = f.Dialer.Dial(network, addr)
if err != nil {
f.IncFailures()
if f.Failures() >= f.MaxFailures() && f.Enabled() {
@ -97,7 +107,7 @@ func (f *Forwarder) Dial(network, addr string) (c net.Conn, err error) {
}
}
return c, err
return c, p, err
}
// Failures returns the failuer count of forwarder

View File

@ -31,7 +31,7 @@ type Config struct {
}
// forwarder slice orderd by priority
type priSlice []*proxy.Forwarder
type priSlice []*Forwarder
func (p priSlice) Len() int { return len(p) }
func (p priSlice) Less(i, j int) bool { return p[i].Priority() > p[j].Priority() }
@ -41,19 +41,19 @@ func (p priSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type Dialer struct {
config *Config
fwdrs priSlice
available []*proxy.Forwarder
available []*Forwarder
mu sync.RWMutex
index uint32
priority uint32
nextForwarder func(addr string) *proxy.Forwarder
nextForwarder func(addr string) *Forwarder
}
// NewDialer returns a new strategy dialer.
func NewDialer(s []string, c *Config) proxy.Dialer {
var fwdrs []*proxy.Forwarder
func NewDialer(s []string, c *Config) *Dialer {
var fwdrs []*Forwarder
for _, chain := range s {
fwdr, err := proxy.ForwarderFromURL(chain, c.IntFace)
fwdr, err := ForwarderFromURL(chain, c.IntFace)
if err != nil {
log.Fatal(err)
}
@ -62,22 +62,16 @@ func NewDialer(s []string, c *Config) proxy.Dialer {
}
if len(fwdrs) == 0 {
d, err := proxy.NewDirect(c.IntFace)
if err != nil {
log.Fatal(err)
}
return d
}
if len(fwdrs) == 1 {
return fwdrs[0]
// direct forwarder
fwdrs = append(fwdrs, DirectForwarder(c.IntFace))
c.Strategy = "rr"
}
return newDialer(fwdrs, c)
}
// newDialer returns a new rrDialer
func newDialer(fwdrs []*proxy.Forwarder, c *Config) *Dialer {
func newDialer(fwdrs []*Forwarder, c *Config) *Dialer {
d := &Dialer{fwdrs: fwdrs, config: c}
sort.Sort(d.fwdrs)
@ -90,19 +84,19 @@ func newDialer(fwdrs []*proxy.Forwarder, c *Config) *Dialer {
switch c.Strategy {
case "rr":
d.nextForwarder = d.scheduleRR
log.F("forward to remote servers in round robin mode.")
log.F("[strategy] forward to remote servers in round robin mode.")
case "ha":
d.nextForwarder = d.scheduleHA
log.F("forward to remote servers in high availability mode.")
log.F("[strategy] forward to remote servers in high availability mode.")
case "lha":
d.nextForwarder = d.scheduleLHA
log.F("forward to remote servers in latency based high availability mode.")
log.F("[strategy] forward to remote servers in latency based high availability mode.")
case "dh":
d.nextForwarder = d.scheduleDH
log.F("forward to remote servers in destination hashing mode.")
log.F("[strategy] forward to remote servers in destination hashing mode.")
default:
d.nextForwarder = d.scheduleRR
log.F("not supported forward mode '%s', use round robin mode.", c.Strategy)
log.F("[strategy] not supported forward mode '%s', use round robin mode.", c.Strategy)
}
for _, f := range fwdrs {
@ -116,8 +110,10 @@ func newDialer(fwdrs []*proxy.Forwarder, c *Config) *Dialer {
func (d *Dialer) Addr() string { return "STRATEGY" }
// Dial connects to the address addr on the network net.
func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
return d.NextDialer(addr).Dial(network, addr)
func (d *Dialer) Dial(network, addr string) (net.Conn, string, error) {
nd := d.NextDialer(addr)
c, _, err := nd.Dial(network, addr)
return c, nd.Addr(), err
}
// DialUDP connects to the given address.
@ -164,7 +160,7 @@ func (d *Dialer) initAvailable() {
}
// onStatusChanged will be called when fwdr's status changed.
func (d *Dialer) onStatusChanged(fwdr *proxy.Forwarder) {
func (d *Dialer) onStatusChanged(fwdr *Forwarder) {
d.mu.Lock()
defer d.mu.Unlock()
@ -192,8 +188,11 @@ func (d *Dialer) onStatusChanged(fwdr *proxy.Forwarder) {
// Check implements the Checker interface.
func (d *Dialer) Check() {
for i := 0; i < len(d.fwdrs); i++ {
go d.check(i)
// no need to check when there's only 1 forwarder
if len(d.fwdrs) > 1 {
for i := 0; i < len(d.fwdrs); i++ {
go d.check(i)
}
}
}
@ -216,7 +215,7 @@ func (d *Dialer) check(i int) {
}
startTime := time.Now()
rc, err := f.Dial("tcp", d.config.CheckWebSite)
rc, _, err := f.Dial("tcp", d.config.CheckWebSite)
if err != nil {
f.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in dial: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, err)
@ -253,17 +252,17 @@ func (d *Dialer) check(i int) {
}
// Round Robin
func (d *Dialer) scheduleRR(dstAddr string) *proxy.Forwarder {
func (d *Dialer) scheduleRR(dstAddr string) *Forwarder {
return d.available[atomic.AddUint32(&d.index, 1)%uint32(len(d.available))]
}
// High Availability
func (d *Dialer) scheduleHA(dstAddr string) *proxy.Forwarder {
func (d *Dialer) scheduleHA(dstAddr string) *Forwarder {
return d.available[0]
}
// Latency based High Availability
func (d *Dialer) scheduleLHA(dstAddr string) *proxy.Forwarder {
func (d *Dialer) scheduleLHA(dstAddr string) *Forwarder {
fwdr := d.available[0]
lowest := fwdr.Latency()
for _, f := range d.available {
@ -276,7 +275,7 @@ func (d *Dialer) scheduleLHA(dstAddr string) *proxy.Forwarder {
}
// Destination Hashing
func (d *Dialer) scheduleDH(dstAddr string) *proxy.Forwarder {
func (d *Dialer) scheduleDH(dstAddr string) *Forwarder {
fnv1a := fnv.New32a()
fnv1a.Write([]byte(dstAddr))
return d.available[fnv1a.Sum32()%uint32(len(d.available))]