mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
strategy: add checktimeout config. (#89)
This commit is contained in:
parent
47406ce4ce
commit
a22b1d9c86
@ -116,9 +116,11 @@ glider -config CONFIGPATH -listen :8080 -verbose
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
glider v0.6.10 usage:
|
glider v0.7.0 usage:
|
||||||
-checkinterval int
|
-checkinterval int
|
||||||
proxy check interval(seconds) (default 30)
|
proxy check interval(seconds) (default 30)
|
||||||
|
-checktimeout int
|
||||||
|
proxy check timeout(seconds) (default 10)
|
||||||
-checkwebsite string
|
-checkwebsite string
|
||||||
proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80 (default "www.apple.com")
|
proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80 (default "www.apple.com")
|
||||||
-config string
|
-config string
|
||||||
@ -139,6 +141,8 @@ glider v0.6.10 usage:
|
|||||||
timeout value used in multiple dnsservers switch(seconds) (default 3)
|
timeout value used in multiple dnsservers switch(seconds) (default 3)
|
||||||
-forward value
|
-forward value
|
||||||
forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]
|
forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]
|
||||||
|
-include value
|
||||||
|
include file
|
||||||
-interface string
|
-interface string
|
||||||
source ip or source interface
|
source ip or source interface
|
||||||
-listen value
|
-listen value
|
||||||
|
@ -7,41 +7,35 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UDPBufSize is the size of udp buffer
|
// UDPBufSize is the size of udp buffer.
|
||||||
const UDPBufSize = 65536
|
const UDPBufSize = 65536
|
||||||
|
|
||||||
// Conn struct
|
// Conn is a base conn struct.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
r *bufio.Reader
|
r *bufio.Reader
|
||||||
net.Conn
|
net.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConn .
|
// NewConn returns a new conn.
|
||||||
func NewConn(c net.Conn) *Conn {
|
func NewConn(c net.Conn) *Conn {
|
||||||
return &Conn{bufio.NewReader(c), c}
|
return &Conn{bufio.NewReader(c), c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConnSize .
|
// Peek returns the next n bytes without advancing the reader.
|
||||||
func NewConnSize(c net.Conn, n int) *Conn {
|
|
||||||
return &Conn{bufio.NewReaderSize(c, n), c}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Peek .
|
|
||||||
func (c *Conn) Peek(n int) ([]byte, error) {
|
func (c *Conn) Peek(n int) ([]byte, error) {
|
||||||
return c.r.Peek(n)
|
return c.r.Peek(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read .
|
|
||||||
func (c *Conn) Read(p []byte) (int, error) {
|
func (c *Conn) Read(p []byte) (int, error) {
|
||||||
return c.r.Read(p)
|
return c.r.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reader returns the internal bufio.Reader
|
// Reader returns the internal bufio.Reader.
|
||||||
func (c *Conn) Reader() *bufio.Reader {
|
func (c *Conn) Reader() *bufio.Reader {
|
||||||
return c.r
|
return c.r
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relay .
|
// Relay relays between left and right.
|
||||||
func Relay(left, right net.Conn) (int64, int64, error) {
|
func Relay(left, right net.Conn) (int64, int64, error) {
|
||||||
type res struct {
|
type res struct {
|
||||||
N int64
|
N int64
|
||||||
@ -67,8 +61,8 @@ func Relay(left, right net.Conn) (int64, int64, error) {
|
|||||||
return n, rs.N, err
|
return n, rs.N, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimedCopy copy from src to dst at target with read timeout
|
// RelayUDP copys from src to dst at target with read timeout.
|
||||||
func TimedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration) error {
|
func RelayUDP(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration) error {
|
||||||
buf := make([]byte, UDPBufSize)
|
buf := make([]byte, UDPBufSize)
|
||||||
for {
|
for {
|
||||||
src.SetReadDeadline(time.Now().Add(timeout))
|
src.SetReadDeadline(time.Now().Add(timeout))
|
||||||
@ -84,7 +78,7 @@ func TimedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutboundIP returns preferred outbound ip of this machine
|
// OutboundIP returns preferred outbound ip of this machine.
|
||||||
func OutboundIP() string {
|
func OutboundIP() string {
|
||||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
1
conf.go
1
conf.go
@ -40,6 +40,7 @@ func confInit() {
|
|||||||
flag.StringVar(&conf.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr")
|
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.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, "checkinterval", 30, "proxy check interval(seconds)")
|
flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkinterval", 30, "proxy check interval(seconds)")
|
||||||
|
flag.IntVar(&conf.StrategyConfig.CheckTimeout, "checktimeout", 10, "proxy check timeout(seconds)")
|
||||||
flag.IntVar(&conf.StrategyConfig.MaxFailures, "maxfailures", 3, "max failures to change forwarder status to disabled")
|
flag.IntVar(&conf.StrategyConfig.MaxFailures, "maxfailures", 3, "max failures to change forwarder status to disabled")
|
||||||
flag.StringVar(&conf.StrategyConfig.IntFace, "interface", "", "source ip or source interface")
|
flag.StringVar(&conf.StrategyConfig.IntFace, "interface", "", "source ip or source interface")
|
||||||
|
|
||||||
|
@ -155,6 +155,8 @@ checkwebsite=www.apple.com
|
|||||||
# check interval(seconds)
|
# check interval(seconds)
|
||||||
checkinterval=30
|
checkinterval=30
|
||||||
|
|
||||||
|
# check timeout(seconds)
|
||||||
|
checktimeout=10
|
||||||
|
|
||||||
# DNS FORWARDING SERVER
|
# DNS FORWARDING SERVER
|
||||||
# ----------------
|
# ----------------
|
||||||
|
6
go.mod
6
go.mod
@ -19,9 +19,9 @@ require (
|
|||||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
|
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
|
||||||
github.com/tjfoc/gmsm v1.0.1 // indirect
|
github.com/tjfoc/gmsm v1.0.1 // indirect
|
||||||
github.com/xtaci/kcp-go v5.0.7+incompatible
|
github.com/xtaci/kcp-go v5.0.7+incompatible
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977 // indirect
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313 // indirect
|
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// Replace dependency modules with local developing copy
|
// Replace dependency modules with local developing copy
|
||||||
|
6
go.sum
6
go.sum
@ -38,6 +38,8 @@ golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28
|
|||||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
|
||||||
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 h1:fY7Dsw114eJN4boqzVSbpVHO6rTdhq6/GnXeu+PKnzU=
|
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 h1:fY7Dsw114eJN4boqzVSbpVHO6rTdhq6/GnXeu+PKnzU=
|
||||||
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190310074541-c10a0554eabf h1:J7RqX9u0J9ZB37CGaFc2VC+QZZT6E6jnDbrboEFVo0U=
|
golang.org/x/net v0.0.0-20190310074541-c10a0554eabf h1:J7RqX9u0J9ZB37CGaFc2VC+QZZT6E6jnDbrboEFVo0U=
|
||||||
@ -46,6 +48,8 @@ golang.org/x/net v0.0.0-20190311031020-56fb01167e7d h1:vQJbQvu6+H699vOmHa20TEBI9
|
|||||||
golang.org/x/net v0.0.0-20190311031020-56fb01167e7d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190311031020-56fb01167e7d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I=
|
||||||
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190302025703-b6889370fb10 h1:xQJI9OEiErEQ++DoXOHqEpzsGMrAv2Q2jyCpi7DmfpQ=
|
golang.org/x/sys v0.0.0-20190302025703-b6889370fb10 h1:xQJI9OEiErEQ++DoXOHqEpzsGMrAv2Q2jyCpi7DmfpQ=
|
||||||
golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -69,4 +73,6 @@ golang.org/x/sys v0.0.0-20190311152110-c8c8c57fd1e1 h1:FQNj2xvjQ1lgFyzbSybGZr792
|
|||||||
golang.org/x/sys v0.0.0-20190311152110-c8c8c57fd1e1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190311152110-c8c8c57fd1e1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f h1:yCrMx/EeIue0+Qca57bWZS7VX6ymEoypmhWyPhz0NHM=
|
||||||
|
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -26,10 +26,10 @@ import (
|
|||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is socks5 version number
|
// Version is socks5 version number.
|
||||||
const Version = 5
|
const Version = 5
|
||||||
|
|
||||||
// SOCKS5 struct
|
// SOCKS5 is a base socks5 struct.
|
||||||
type SOCKS5 struct {
|
type SOCKS5 struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
addr string
|
addr string
|
||||||
@ -43,7 +43,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSOCKS5 returns a Proxy that makes SOCKS v5 connections to the given address
|
// NewSOCKS5 returns a Proxy that makes SOCKS v5 connections to the given address
|
||||||
// with an optional username and password. See RFC 1928
|
// with an optional username and password. (RFC 1928)
|
||||||
func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -65,23 +65,23 @@ func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
|||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSocks5Dialer returns a socks5 proxy dialer
|
// NewSocks5Dialer returns a socks5 proxy dialer.
|
||||||
func NewSocks5Dialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
func NewSocks5Dialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
return NewSOCKS5(s, dialer)
|
return NewSOCKS5(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSocks5Server returns a socks5 proxy server
|
// NewSocks5Server returns a socks5 proxy server.
|
||||||
func NewSocks5Server(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewSocks5Server(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewSOCKS5(s, dialer)
|
return NewSOCKS5(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe serves socks5 requests
|
// ListenAndServe serves socks5 requests.
|
||||||
func (s *SOCKS5) ListenAndServe() {
|
func (s *SOCKS5) ListenAndServe() {
|
||||||
go s.ListenAndServeUDP()
|
go s.ListenAndServeUDP()
|
||||||
s.ListenAndServeTCP()
|
s.ListenAndServeTCP()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeTCP .
|
// ListenAndServeTCP listen and serve on tcp port.
|
||||||
func (s *SOCKS5) ListenAndServeTCP() {
|
func (s *SOCKS5) ListenAndServeTCP() {
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -102,7 +102,7 @@ func (s *SOCKS5) ListenAndServeTCP() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve .
|
// Serve serves a connection.
|
||||||
func (s *SOCKS5) Serve(c net.Conn) {
|
func (s *SOCKS5) Serve(c net.Conn) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ func (s *SOCKS5) Serve(c net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeUDP serves udp requests
|
// ListenAndServeUDP serves udp requests.
|
||||||
func (s *SOCKS5) ListenAndServeUDP() {
|
func (s *SOCKS5) ListenAndServeUDP() {
|
||||||
lc, err := net.ListenPacket("udp", s.addr)
|
lc, err := net.ListenPacket("udp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -189,7 +189,7 @@ func (s *SOCKS5) ListenAndServeUDP() {
|
|||||||
nm.Store(raddr.String(), pc)
|
nm.Store(raddr.String(), pc)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
conn.RelayUDP(c, raddr, pc, 2*time.Minute)
|
||||||
pc.Close()
|
pc.Close()
|
||||||
nm.Delete(raddr.String())
|
nm.Delete(raddr.String())
|
||||||
}()
|
}()
|
||||||
@ -211,7 +211,7 @@ func (s *SOCKS5) ListenAndServeUDP() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Addr returns forwarder's address
|
// Addr returns forwarder's address.
|
||||||
func (s *SOCKS5) Addr() string {
|
func (s *SOCKS5) Addr() string {
|
||||||
if s.addr == "" {
|
if s.addr == "" {
|
||||||
return s.dialer.Addr()
|
return s.dialer.Addr()
|
||||||
@ -219,7 +219,7 @@ func (s *SOCKS5) Addr() string {
|
|||||||
return s.addr
|
return s.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextDialer returns the next dialer
|
// NextDialer returns the next dialer.
|
||||||
func (s *SOCKS5) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
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.
|
// Dial connects to the address addr on the network net via the SOCKS5 proxy.
|
||||||
@ -244,7 +244,7 @@ func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the given address via the proxy
|
// DialUDP connects to the given address via the proxy.
|
||||||
func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
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 {
|
if err != nil {
|
||||||
@ -293,7 +293,7 @@ func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A
|
|||||||
|
|
||||||
// connect takes an existing connection to a socks5 proxy server,
|
// connect takes an existing connection to a socks5 proxy server,
|
||||||
// and commands the server to extend that connection to target,
|
// and commands the server to extend that connection to target,
|
||||||
// which must be a canonical address with a host and port
|
// which must be a canonical address with a host and port.
|
||||||
func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
||||||
host, portStr, err := net.SplitHostPort(target)
|
host, portStr, err := net.SplitHostPort(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -424,7 +424,7 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handshake fast-tracks SOCKS initialization to get target address to connect
|
// Handshake fast-tracks SOCKS initialization to get target address to connect.
|
||||||
func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
||||||
// Read RFC 1928 for request and reply structure and sizes
|
// Read RFC 1928 for request and reply structure and sizes
|
||||||
buf := make([]byte, socks.MaxAddrLen)
|
buf := make([]byte, socks.MaxAddrLen)
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SS .
|
// SS is a base ss struct.
|
||||||
type SS struct {
|
type SS struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
addr string
|
addr string
|
||||||
@ -29,7 +29,7 @@ func init() {
|
|||||||
proxy.RegisterServer("ss", NewSSServer)
|
proxy.RegisterServer("ss", NewSSServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSS returns a shadowsocks proxy
|
// NewSS returns a ss proxy.
|
||||||
func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -55,23 +55,23 @@ func NewSS(s string, dialer proxy.Dialer) (*SS, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSSDialer returns a ss proxy dialer
|
// NewSSDialer returns a ss proxy dialer.
|
||||||
func NewSSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
func NewSSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
return NewSS(s, dialer)
|
return NewSS(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSSServer returns a ss proxy server
|
// NewSSServer returns a ss proxy server.
|
||||||
func NewSSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewSSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewSS(s, dialer)
|
return NewSS(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe serves ss requests
|
// ListenAndServe serves ss requests.
|
||||||
func (s *SS) ListenAndServe() {
|
func (s *SS) ListenAndServe() {
|
||||||
go s.ListenAndServeUDP()
|
go s.ListenAndServeUDP()
|
||||||
s.ListenAndServeTCP()
|
s.ListenAndServeTCP()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeTCP serves tcp ss requests
|
// ListenAndServeTCP serves tcp ss requests.
|
||||||
func (s *SS) ListenAndServeTCP() {
|
func (s *SS) ListenAndServeTCP() {
|
||||||
l, err := net.Listen("tcp", s.addr)
|
l, err := net.Listen("tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -92,7 +92,7 @@ func (s *SS) ListenAndServeTCP() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve serves tcp ss requests
|
// Serve serves a connection.
|
||||||
func (s *SS) Serve(c net.Conn) {
|
func (s *SS) Serve(c net.Conn) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ func (s *SS) Serve(c net.Conn) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServeUDP serves udp ss requests
|
// ListenAndServeUDP serves udp ss requests.
|
||||||
func (s *SS) ListenAndServeUDP() {
|
func (s *SS) ListenAndServeUDP() {
|
||||||
lc, err := net.ListenPacket("udp", s.addr)
|
lc, err := net.ListenPacket("udp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -204,7 +204,7 @@ func (s *SS) ListenAndServeUDP() {
|
|||||||
nm.Store(raddr.String(), pc)
|
nm.Store(raddr.String(), pc)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
conn.RelayUDP(c, raddr, pc, 2*time.Minute)
|
||||||
pc.Close()
|
pc.Close()
|
||||||
nm.Delete(raddr.String())
|
nm.Delete(raddr.String())
|
||||||
}()
|
}()
|
||||||
@ -225,12 +225,12 @@ func (s *SS) ListenAndServeUDP() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCipher .
|
// ListCipher returns all the ciphers supported.
|
||||||
func ListCipher() string {
|
func ListCipher() string {
|
||||||
return strings.Join(core.ListCipher(), " ")
|
return strings.Join(core.ListCipher(), " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Addr returns forwarder's address
|
// Addr returns forwarder's address.
|
||||||
func (s *SS) Addr() string {
|
func (s *SS) Addr() string {
|
||||||
if s.addr == "" {
|
if s.addr == "" {
|
||||||
return s.dialer.Addr()
|
return s.dialer.Addr()
|
||||||
@ -238,7 +238,7 @@ func (s *SS) Addr() string {
|
|||||||
return s.addr
|
return s.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextDialer returns the next dialer
|
// NextDialer returns the next dialer.
|
||||||
func (s *SS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
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.
|
// Dial connects to the address addr on the network net via the proxy.
|
||||||
@ -268,7 +268,7 @@ func (s *SS) Dial(network, addr string) (net.Conn, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the given address via the proxy
|
// DialUDP connects to the given address via the proxy.
|
||||||
func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||||
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -16,8 +16,8 @@ import (
|
|||||||
type UDPTun struct {
|
type UDPTun struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
addr string
|
addr string
|
||||||
taddr string // tunnel addr
|
taddr string // tunnel addr string
|
||||||
uaddr *net.UDPAddr // tunnel addr
|
tuaddr *net.UDPAddr // tunnel addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -41,7 +41,7 @@ func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) {
|
|||||||
taddr: d[1],
|
taddr: d[1],
|
||||||
}
|
}
|
||||||
|
|
||||||
p.uaddr, err = net.ResolveUDPAddr("udp", p.taddr)
|
p.tuaddr, err = net.ResolveUDPAddr("udp", p.taddr)
|
||||||
return p, err
|
return p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ func (s *UDPTun) ListenAndServe() {
|
|||||||
nm.Store(raddr.String(), pc)
|
nm.Store(raddr.String(), pc)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
conn.RelayUDP(c, raddr, pc, 2*time.Minute)
|
||||||
pc.Close()
|
pc.Close()
|
||||||
nm.Delete(raddr.String())
|
nm.Delete(raddr.String())
|
||||||
}()
|
}()
|
||||||
@ -94,7 +94,7 @@ func (s *UDPTun) ListenAndServe() {
|
|||||||
pc = v.(net.PacketConn)
|
pc = v.(net.PacketConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = pc.WriteTo(buf[:n], s.uaddr)
|
_, err = pc.WriteTo(buf[:n], s.tuaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[udptun] remote write error: %v", err)
|
log.F("[udptun] remote write error: %v", err)
|
||||||
continue
|
continue
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UoTTun udp over tcp tunnel
|
// UoTTun is a base udp over tcp tunnel struct.
|
||||||
type UoTTun struct {
|
type UoTTun struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
addr string
|
addr string
|
||||||
@ -24,7 +24,7 @@ func init() {
|
|||||||
proxy.RegisterServer("uottun", NewUoTTunServer)
|
proxy.RegisterServer("uottun", NewUoTTunServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUoTTun returns a UoTTun proxy
|
// NewUoTTun returns a UoTTun proxy.
|
||||||
func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -44,12 +44,12 @@ func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUoTTunServer returns a uot tunnel server
|
// NewUoTTunServer returns a uot tunnel server.
|
||||||
func NewUoTTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
func NewUoTTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||||
return NewUoTTun(s, dialer)
|
return NewUoTTun(s, dialer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenAndServe .
|
// ListenAndServe listen and serve on tcp.
|
||||||
func (s *UoTTun) ListenAndServe() {
|
func (s *UoTTun) ListenAndServe() {
|
||||||
c, err := net.ListenPacket("udp", s.addr)
|
c, err := net.ListenPacket("udp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -78,7 +78,7 @@ func (s *UoTTun) ListenAndServe() {
|
|||||||
go func() {
|
go func() {
|
||||||
// no remote forwarder, just a local udp forwarder
|
// no remote forwarder, just a local udp forwarder
|
||||||
if urc, ok := rc.(*net.UDPConn); ok {
|
if urc, ok := rc.(*net.UDPConn); ok {
|
||||||
conn.TimedCopy(c, clientAddr, urc, 2*time.Minute)
|
conn.RelayUDP(c, clientAddr, urc, 2*time.Minute)
|
||||||
urc.Close()
|
urc.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ func (s *UoTTun) ListenAndServe() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve .
|
// Serve is not allowed to be called directly.
|
||||||
func (s *UoTTun) Serve(c net.Conn) {
|
func (s *UoTTun) Serve(c net.Conn) {
|
||||||
// TODO
|
// TODO
|
||||||
log.F("[uottun] func Serve: can not be called directly")
|
log.F("[uottun] func Serve: can not be called directly")
|
||||||
|
@ -35,6 +35,7 @@ func NewConfFromFile(ruleFile string) (*Config, error) {
|
|||||||
f.StringVar(&p.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr")
|
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.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, "checkinterval", 30, "proxy check interval(seconds)")
|
f.IntVar(&p.StrategyConfig.CheckInterval, "checkinterval", 30, "proxy check interval(seconds)")
|
||||||
|
f.IntVar(&p.StrategyConfig.CheckTimeout, "checktimeout", 10, "proxy check timeout(seconds)")
|
||||||
f.StringVar(&p.StrategyConfig.IntFace, "interface", "", "source ip or source interface")
|
f.StringVar(&p.StrategyConfig.IntFace, "interface", "", "source ip or source interface")
|
||||||
|
|
||||||
f.StringSliceUniqVar(&p.DNSServers, "dnsserver", nil, "remote dns server")
|
f.StringSliceUniqVar(&p.DNSServers, "dnsserver", nil, "remote dns server")
|
||||||
|
@ -15,16 +15,17 @@ import (
|
|||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checker is an interface of forwarder checker
|
// Checker is an interface of forwarder checker.
|
||||||
type Checker interface {
|
type Checker interface {
|
||||||
Check()
|
Check()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config of strategy
|
// Config is strategy config struct.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Strategy string
|
Strategy string
|
||||||
CheckWebSite string
|
CheckWebSite string
|
||||||
CheckInterval int
|
CheckInterval int
|
||||||
|
CheckTimeout int
|
||||||
MaxFailures int
|
MaxFailures int
|
||||||
IntFace string
|
IntFace string
|
||||||
}
|
}
|
||||||
@ -36,7 +37,7 @@ func (p priSlice) Len() int { return len(p) }
|
|||||||
func (p priSlice) Less(i, j int) bool { return p[i].Priority() > p[j].Priority() }
|
func (p priSlice) Less(i, j int) bool { return p[i].Priority() > p[j].Priority() }
|
||||||
func (p priSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
func (p priSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||||
|
|
||||||
// Dialer .
|
// Dialer is base dialer struct.
|
||||||
type Dialer struct {
|
type Dialer struct {
|
||||||
config *Config
|
config *Config
|
||||||
fwdrs priSlice
|
fwdrs priSlice
|
||||||
@ -48,7 +49,7 @@ type Dialer struct {
|
|||||||
nextForwarder func(addr string) *proxy.Forwarder
|
nextForwarder func(addr string) *proxy.Forwarder
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDialer returns a new strategy dialer
|
// NewDialer returns a new strategy dialer.
|
||||||
func NewDialer(s []string, c *Config) proxy.Dialer {
|
func NewDialer(s []string, c *Config) proxy.Dialer {
|
||||||
var fwdrs []*proxy.Forwarder
|
var fwdrs []*proxy.Forwarder
|
||||||
for _, chain := range s {
|
for _, chain := range s {
|
||||||
@ -111,20 +112,20 @@ func newDialer(fwdrs []*proxy.Forwarder, c *Config) *Dialer {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// Addr returns forwarder's address
|
// Addr returns forwarder's address.
|
||||||
func (d *Dialer) Addr() string { return "STRATEGY" }
|
func (d *Dialer) Addr() string { return "STRATEGY" }
|
||||||
|
|
||||||
// Dial connects to the address addr on the network net
|
// Dial connects to the address addr on the network net.
|
||||||
func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
|
func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||||
return d.NextDialer(addr).Dial(network, addr)
|
return d.NextDialer(addr).Dial(network, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the given address
|
// DialUDP connects to the given address.
|
||||||
func (d *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
func (d *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||||
return d.NextDialer(addr).DialUDP(network, addr)
|
return d.NextDialer(addr).DialUDP(network, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextDialer returns the next dialer
|
// NextDialer returns the next dialer.
|
||||||
func (d *Dialer) NextDialer(dstAddr string) proxy.Dialer {
|
func (d *Dialer) NextDialer(dstAddr string) proxy.Dialer {
|
||||||
d.mu.RLock()
|
d.mu.RLock()
|
||||||
defer d.mu.RUnlock()
|
defer d.mu.RUnlock()
|
||||||
@ -132,13 +133,13 @@ func (d *Dialer) NextDialer(dstAddr string) proxy.Dialer {
|
|||||||
return d.nextForwarder(dstAddr)
|
return d.nextForwarder(dstAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priority returns the active priority of dialer
|
// Priority returns the active priority of dialer.
|
||||||
func (d *Dialer) Priority() uint32 { return atomic.LoadUint32(&d.priority) }
|
func (d *Dialer) Priority() uint32 { return atomic.LoadUint32(&d.priority) }
|
||||||
|
|
||||||
// SetPriority sets the active priority of daler
|
// SetPriority sets the active priority of daler.
|
||||||
func (d *Dialer) SetPriority(p uint32) { atomic.StoreUint32(&d.priority, p) }
|
func (d *Dialer) SetPriority(p uint32) { atomic.StoreUint32(&d.priority, p) }
|
||||||
|
|
||||||
// initAvailable traverse d.fwdrs and init the available forwarder slice
|
// initAvailable traverse d.fwdrs and init the available forwarder slice.
|
||||||
func (d *Dialer) initAvailable() {
|
func (d *Dialer) initAvailable() {
|
||||||
for _, f := range d.fwdrs {
|
for _, f := range d.fwdrs {
|
||||||
if f.Enabled() {
|
if f.Enabled() {
|
||||||
@ -162,7 +163,7 @@ func (d *Dialer) initAvailable() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// onStatusChanged will be called when fwdr's status changed
|
// onStatusChanged will be called when fwdr's status changed.
|
||||||
func (d *Dialer) onStatusChanged(fwdr *proxy.Forwarder) {
|
func (d *Dialer) onStatusChanged(fwdr *proxy.Forwarder) {
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
@ -189,7 +190,7 @@ func (d *Dialer) onStatusChanged(fwdr *proxy.Forwarder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check implements the Checker interface
|
// Check implements the Checker interface.
|
||||||
func (d *Dialer) Check() {
|
func (d *Dialer) Check() {
|
||||||
for i := 0; i < len(d.fwdrs); i++ {
|
for i := 0; i < len(d.fwdrs); i++ {
|
||||||
go d.check(i)
|
go d.check(i)
|
||||||
@ -229,11 +230,19 @@ func (d *Dialer) check(i int) {
|
|||||||
f.Disable()
|
f.Disable()
|
||||||
log.F("[check] %s(%d) -> %s, DISABLED. error in read: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, err)
|
log.F("[check] %s(%d) -> %s, DISABLED. error in read: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, err)
|
||||||
} else if bytes.Equal([]byte("HTTP"), buf) {
|
} else if bytes.Equal([]byte("HTTP"), buf) {
|
||||||
f.Enable()
|
|
||||||
retry = 2
|
|
||||||
readTime := time.Since(startTime)
|
readTime := time.Since(startTime)
|
||||||
f.SetLatency(int64(readTime))
|
f.SetLatency(int64(readTime))
|
||||||
log.F("[check] %s(%d) -> %s, ENABLED. connect time: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, readTime.String())
|
|
||||||
|
if readTime > time.Duration(d.config.CheckTimeout)*time.Second {
|
||||||
|
f.Disable()
|
||||||
|
log.F("[check] %s(%d) -> %s, DISABLED. connect timeout: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, readTime)
|
||||||
|
} else {
|
||||||
|
retry = 2
|
||||||
|
f.Enable()
|
||||||
|
log.F("[check] %s(%d) -> %s, ENABLED. connect time: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, readTime)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
f.Disable()
|
f.Disable()
|
||||||
log.F("[check] %s(%d) -> %s, DISABLED. server response: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, buf)
|
log.F("[check] %s(%d) -> %s, DISABLED. server response: %s", f.Addr(), f.Priority(), d.config.CheckWebSite, buf)
|
||||||
|
Loading…
Reference in New Issue
Block a user