mirror of
				https://github.com/nadoo/glider.git
				synced 2025-11-04 15:52:38 +08:00 
			
		
		
		
	1. general: restructured codes;
2. rule: add dnsserver to ip rule map; 3. dns: add DNSAnswerHandler so we can define a custom handler when a domain resolved
This commit is contained in:
		
							parent
							
								
									dd5ba37582
								
							
						
					
					
						commit
						652d49182a
					
				@ -30,7 +30,8 @@ General:
 | 
			
		||||
- Rule proxy based on destionation: [Config Examples](examples)
 | 
			
		||||
 | 
			
		||||
TODO:
 | 
			
		||||
- Specify different remote dns server in rule file
 | 
			
		||||
- Specify different remote dns server in rule file (DONE)
 | 
			
		||||
- Improve DNS forwarder to resolve domain name and add ip to proxy rules (WIP)
 | 
			
		||||
- IPSet management
 | 
			
		||||
- Improve DNS forwarder to resolve domain name and add ip to ipset
 | 
			
		||||
- TUN/TAP device support
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										197
									
								
								conf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								conf.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,197 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/nadoo/conflag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var flag = conflag.New()
 | 
			
		||||
 | 
			
		||||
var conf struct {
 | 
			
		||||
	Verbose       bool
 | 
			
		||||
	Strategy      string
 | 
			
		||||
	CheckWebSite  string
 | 
			
		||||
	CheckDuration int
 | 
			
		||||
	Listen        []string
 | 
			
		||||
	Forward       []string
 | 
			
		||||
	RuleFile      []string
 | 
			
		||||
	RulesDir      string
 | 
			
		||||
 | 
			
		||||
	DNS       string
 | 
			
		||||
	DNSServer []string
 | 
			
		||||
 | 
			
		||||
	IPSet string
 | 
			
		||||
 | 
			
		||||
	rules []*RuleConf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.CheckDuration, "checkduration", 30, "proxy check duration(seconds)")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.Listen, "listen", nil, "listen url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.Forward, "forward", nil, "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.RuleFile, "rulefile", nil, "rule file path")
 | 
			
		||||
	flag.StringVar(&conf.RulesDir, "rules-dir", "rules.d", "rule file folder")
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&conf.DNS, "dns", "", "dns listen address")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.DNSServer, "dnsserver", []string{"8.8.8.8:53"}, "remote dns server")
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&conf.IPSet, "ipset", "glider", "ipset name")
 | 
			
		||||
 | 
			
		||||
	flag.Usage = usage
 | 
			
		||||
	err := flag.Parse()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
 | 
			
		||||
		os.Exit(-1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(conf.Listen) == 0 && conf.DNS == "" {
 | 
			
		||||
		flag.Usage()
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "ERROR: listen url must be specified.\n")
 | 
			
		||||
		os.Exit(-1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// rulefiles
 | 
			
		||||
	for _, ruleFile := range conf.RuleFile {
 | 
			
		||||
		rule, err := NewRuleConfFromFile(ruleFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		conf.rules = append(conf.rules, rule)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ruleFolderFiles, _ := listDir(conf.RulesDir, ".rule")
 | 
			
		||||
	for _, ruleFile := range ruleFolderFiles {
 | 
			
		||||
		rule, err := NewRuleConfFromFile(ruleFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		conf.rules = append(conf.rules, rule)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RuleConf , every ruleForwarder points to a rule file
 | 
			
		||||
type RuleConf struct {
 | 
			
		||||
	Forward       []string
 | 
			
		||||
	Strategy      string
 | 
			
		||||
	CheckWebSite  string
 | 
			
		||||
	CheckDuration int
 | 
			
		||||
 | 
			
		||||
	DNSServer []string
 | 
			
		||||
	IPSet     string
 | 
			
		||||
 | 
			
		||||
	Domain []string
 | 
			
		||||
	IP     []string
 | 
			
		||||
	CIDR   []string
 | 
			
		||||
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRuleConfFromFile .
 | 
			
		||||
func NewRuleConfFromFile(ruleFile string) (*RuleConf, error) {
 | 
			
		||||
	p := &RuleConf{name: ruleFile}
 | 
			
		||||
 | 
			
		||||
	f := conflag.NewFromFile("rule", ruleFile)
 | 
			
		||||
	f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
 | 
			
		||||
	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.CheckDuration, "checkduration", 30, "proxy check duration(seconds)")
 | 
			
		||||
 | 
			
		||||
	f.StringSliceUniqVar(&p.DNSServer, "dnsserver", nil, "remote dns server")
 | 
			
		||||
	f.StringVar(&p.IPSet, "ipset", "", "ipset name")
 | 
			
		||||
 | 
			
		||||
	f.StringSliceUniqVar(&p.Domain, "domain", nil, "domain")
 | 
			
		||||
	f.StringSliceUniqVar(&p.IP, "ip", nil, "ip")
 | 
			
		||||
	f.StringSliceUniqVar(&p.CIDR, "cidr", nil, "cidr")
 | 
			
		||||
 | 
			
		||||
	err := f.Parse()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func usage() {
 | 
			
		||||
	app := os.Args[0]
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "%s v%s usage:\n", app, VERSION)
 | 
			
		||||
	flag.PrintDefaults()
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available Schemas:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  mixed: serve as a http/socks5 proxy on the same port. (default)\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  ss: ss proxy\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  socks5: socks5 proxy\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  http: http proxy\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  redir: redirect proxy. (used on linux as a transparent proxy with iptables redirect rules)\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  tcptun: a simple tcp tunnel\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  dnstun: listen on udp port and forward all dns requests to remote dns server via forwarders(tcp)\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available schemas for different modes:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  listen: mixed ss socks5 http redir tcptun dnstun\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  forward: ss socks5 http\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available methods for ss:\n")
 | 
			
		||||
	// fmt.Fprintf(os.Stderr, "  "+ListCipher())
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  NOTE: chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available forward strategies:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  rr: Round Robin mode\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  ha: High Availability mode\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Config file format(see `"+app+".conf.example` as an example):\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  # COMMENT LINE\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  KEY=VALUE\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  KEY=VALUE\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  # KEY equals to command line flag name: listen forward strategy...\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Examples:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -config glider.conf\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -run glider with specified config file.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -config glider.conf -rulefile office.rule -rulefile home.rule\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -run glider with specified global config file and rule config files.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen :8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :8443, serve as http/socks5 proxy on the same port.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on 0.0.0.0:8443 as a ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen socks5://:1080 -verbose\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1080 as a socks5 proxy server, in verbose mode.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen http://:8080 -forward socks5://127.0.0.1:1080\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :8080 as a http proxy server, forward all requests via socks5 server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen redir://:1081 -forward ss://method:pass@1.1.1.1:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1081 as a transparent redirect server, forward all requests via remote ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen socks5://:1080 -listen http://:8080 -forward ss://method:pass@1.1.1.1:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1080 as socks5 server, :8080 as http proxy server, forward all requests via remote ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen redir://:1081 -listen dnstun://:53=8.8.8.8:53 -forward ss://method:pass@server1:port1,ss://method:pass@server2:port2\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1081 as transparent redirect server, :53 as dns server, use forward chain: server1 -> server2.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen socks5://:1080 -forward ss://method:pass@server1:port1 -forward ss://method:pass@server2:port2 -strategy rr\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1080 as socks5 server, forward requests via server1 and server2 in roundrbin mode.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								dialer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								dialer.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Dialer means to establish a connection and relay it.
 | 
			
		||||
type Dialer interface {
 | 
			
		||||
	// Addr()
 | 
			
		||||
	Addr() string
 | 
			
		||||
 | 
			
		||||
	// Dial connects to the given address via the proxy.
 | 
			
		||||
	Dial(network, addr string) (c net.Conn, err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DialerFromURL parses url and get a Proxy
 | 
			
		||||
// TODO: table
 | 
			
		||||
func DialerFromURL(s string, cDialer Dialer) (Dialer, error) {
 | 
			
		||||
	u, err := url.Parse(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("parse err: %s", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addr := u.Host
 | 
			
		||||
	var user, pass string
 | 
			
		||||
	if u.User != nil {
 | 
			
		||||
		user = u.User.Username()
 | 
			
		||||
		pass, _ = u.User.Password()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cDialer == nil {
 | 
			
		||||
		cDialer = Direct
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch u.Scheme {
 | 
			
		||||
	case "http":
 | 
			
		||||
		return NewHTTP(addr, cDialer, nil)
 | 
			
		||||
	case "socks5":
 | 
			
		||||
		return NewSOCKS5("tcp", addr, user, pass, cDialer, nil)
 | 
			
		||||
	case "ss":
 | 
			
		||||
		p, err := NewSS(addr, user, pass, cDialer, nil)
 | 
			
		||||
		return p, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errors.New("unknown schema '" + u.Scheme + "'")
 | 
			
		||||
}
 | 
			
		||||
@ -9,14 +9,7 @@ type direct struct {
 | 
			
		||||
// Direct proxy
 | 
			
		||||
var Direct = &direct{}
 | 
			
		||||
 | 
			
		||||
func (d *direct) Addr() string                  { return "127.0.0.1" }
 | 
			
		||||
func (d *direct) ListenAndServe()               { logf("direct proxy ListenAndServe") }
 | 
			
		||||
func (d *direct) Serve(c net.Conn)              { logf("direct proxy Serve") }
 | 
			
		||||
func (d *direct) CurrentProxy() Proxy           { return d }
 | 
			
		||||
func (d *direct) GetProxy(dstAddr string) Proxy { return d }
 | 
			
		||||
func (d *direct) NextProxy() Proxy              { return d }
 | 
			
		||||
func (d *direct) Enabled() bool                 { return true }
 | 
			
		||||
func (d *direct) SetEnable(enable bool)         {}
 | 
			
		||||
func (d *direct) Addr() string { return "DIRECT" }
 | 
			
		||||
 | 
			
		||||
func (d *direct) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	c, err := net.Dial(network, addr)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										34
									
								
								dns.go
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								dns.go
									
									
									
									
									
								
							@ -38,7 +38,7 @@ type dnsQuery struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dnsAnswer struct {
 | 
			
		||||
	DomainName string
 | 
			
		||||
	// DomainName string
 | 
			
		||||
	QueryType  uint16
 | 
			
		||||
	QueryClass uint16
 | 
			
		||||
	TTL        uint32
 | 
			
		||||
@ -48,18 +48,26 @@ type dnsAnswer struct {
 | 
			
		||||
	IP string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DNSAnswerHandler .
 | 
			
		||||
type DNSAnswerHandler func(domain, ip string) error
 | 
			
		||||
 | 
			
		||||
// DNS .
 | 
			
		||||
type DNS struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
	*Forwarder        // as proxy client
 | 
			
		||||
	sDialer    Dialer // dialer for server
 | 
			
		||||
 | 
			
		||||
	dnsServer string
 | 
			
		||||
 | 
			
		||||
	dnsServerMap map[string]string
 | 
			
		||||
	dnsServerMap   map[string]string
 | 
			
		||||
	answerHandlers []DNSAnswerHandler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDNS returns a dns forwarder. client -> dns.udp -> glider -> forwarder -> remote dns addr
 | 
			
		||||
func NewDNS(addr, raddr string, upProxy Proxy) (*DNS, error) {
 | 
			
		||||
func NewDNS(addr, raddr string, sDialer Dialer) (*DNS, error) {
 | 
			
		||||
	s := &DNS{
 | 
			
		||||
		proxy:        NewProxy(addr, upProxy),
 | 
			
		||||
		Forwarder: NewForwarder(addr, nil),
 | 
			
		||||
		sDialer:   sDialer,
 | 
			
		||||
 | 
			
		||||
		dnsServer:    raddr,
 | 
			
		||||
		dnsServerMap: make(map[string]string),
 | 
			
		||||
	}
 | 
			
		||||
@ -95,8 +103,9 @@ func (s *DNS) ListenAndServe() {
 | 
			
		||||
			domain := query.DomainName
 | 
			
		||||
 | 
			
		||||
			dnsServer := s.GetServer(domain)
 | 
			
		||||
			// TODO: check here
 | 
			
		||||
			rc, err := s.GetProxy(domain+":53").Dial("tcp", dnsServer)
 | 
			
		||||
 | 
			
		||||
			// TODO: check here; ADD dnsServer to rule ip lists
 | 
			
		||||
			rc, err := s.sDialer.Dial("tcp", dnsServer)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logf("failed to connect to server %v: %v", dnsServer, err)
 | 
			
		||||
				return
 | 
			
		||||
@ -129,6 +138,10 @@ func (s *DNS) ListenAndServe() {
 | 
			
		||||
							ip += answer.IP + ","
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						for _, h := range s.answerHandlers {
 | 
			
		||||
							h(query.DomainName, answer.IP)
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
@ -139,7 +152,7 @@ func (s *DNS) ListenAndServe() {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			logf("proxy-dns %s, %s <-> %s, ip: %s", domain, clientAddr.String(), dnsServer, ip)
 | 
			
		||||
			logf("proxy-dns %s <-> %s, %s: %s", clientAddr.String(), dnsServer, domain, ip)
 | 
			
		||||
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
@ -166,6 +179,11 @@ func (s *DNS) GetServer(domain string) string {
 | 
			
		||||
	return s.dnsServer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddAnswerHandler .
 | 
			
		||||
func (s *DNS) AddAnswerHandler(h DNSAnswerHandler) {
 | 
			
		||||
	s.answerHandlers = append(s.answerHandlers, h)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseQuery(p []byte) *dnsQuery {
 | 
			
		||||
	q := &dnsQuery{}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								dnstun.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								dnstun.go
									
									
									
									
									
								
							@ -4,22 +4,26 @@ package main
 | 
			
		||||
 | 
			
		||||
// DNSTun .
 | 
			
		||||
type DNSTun struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
	*Forwarder        // as client
 | 
			
		||||
	sDialer    Dialer // dialer for server
 | 
			
		||||
 | 
			
		||||
	raddr string
 | 
			
		||||
 | 
			
		||||
	udp Proxy
 | 
			
		||||
	tcp Proxy
 | 
			
		||||
	udp *DNS
 | 
			
		||||
	tcp *TCPTun
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDNSTun returns a dns forwarder.
 | 
			
		||||
func NewDNSTun(addr, raddr string, upProxy Proxy) (*DNSTun, error) {
 | 
			
		||||
func NewDNSTun(addr, raddr string, sDialer Dialer) (*DNSTun, error) {
 | 
			
		||||
	s := &DNSTun{
 | 
			
		||||
		proxy: NewProxy(addr, upProxy),
 | 
			
		||||
		Forwarder: NewForwarder(addr, nil),
 | 
			
		||||
		sDialer:   sDialer,
 | 
			
		||||
 | 
			
		||||
		raddr: raddr,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.udp, _ = NewDNS(addr, raddr, upProxy)
 | 
			
		||||
	s.tcp, _ = NewTCPTun(addr, raddr, upProxy)
 | 
			
		||||
	s.udp, _ = NewDNS(addr, raddr, sDialer)
 | 
			
		||||
	s.tcp, _ = NewTCPTun(addr, raddr, sDialer)
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								forwarder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								forwarder.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "net"
 | 
			
		||||
 | 
			
		||||
// Forwarder .
 | 
			
		||||
type Forwarder struct {
 | 
			
		||||
	addr    string
 | 
			
		||||
	cDialer Dialer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewForwarder .
 | 
			
		||||
func NewForwarder(addr string, cDialer Dialer) *Forwarder {
 | 
			
		||||
	if cDialer == nil {
 | 
			
		||||
		cDialer = Direct
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Forwarder{addr: addr, cDialer: cDialer}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Forwarder) Addr() string { return p.addr }
 | 
			
		||||
 | 
			
		||||
func (p *Forwarder) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return p.cDialer.Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								http.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								http.go
									
									
									
									
									
								
							@ -16,22 +16,24 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HTTPProxy .
 | 
			
		||||
type HTTPProxy struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
// HTTP .
 | 
			
		||||
type HTTP struct {
 | 
			
		||||
	*Forwarder        // as client
 | 
			
		||||
	sDialer    Dialer // dialer for server
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewHTTPProxy returns a http proxy.
 | 
			
		||||
func NewHTTPProxy(addr string, upProxy Proxy) (*HTTPProxy, error) {
 | 
			
		||||
	s := &HTTPProxy{
 | 
			
		||||
		proxy: NewProxy(addr, upProxy),
 | 
			
		||||
// NewHTTP returns a http proxy.
 | 
			
		||||
func NewHTTP(addr string, cDialer Dialer, sDialer Dialer) (*HTTP, error) {
 | 
			
		||||
	s := &HTTP{
 | 
			
		||||
		Forwarder: NewForwarder(addr, cDialer),
 | 
			
		||||
		sDialer:   sDialer,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenAndServe .
 | 
			
		||||
func (s *HTTPProxy) ListenAndServe() {
 | 
			
		||||
func (s *HTTP) ListenAndServe() {
 | 
			
		||||
	l, err := net.Listen("tcp", s.addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("failed to listen on %s: %v", s.addr, err)
 | 
			
		||||
@ -53,7 +55,7 @@ func (s *HTTPProxy) ListenAndServe() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serve .
 | 
			
		||||
func (s *HTTPProxy) Serve(c net.Conn) {
 | 
			
		||||
func (s *HTTP) Serve(c net.Conn) {
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
 | 
			
		||||
	if c, ok := c.(*net.TCPConn); ok {
 | 
			
		||||
@ -92,7 +94,7 @@ func (s *HTTPProxy) Serve(c net.Conn) {
 | 
			
		||||
		tgt += ":80"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc, err := s.GetProxy(tgt).Dial("tcp", tgt)
 | 
			
		||||
	rc, err := s.sDialer.Dial("tcp", tgt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
 | 
			
		||||
		logf("failed to dial: %v", err)
 | 
			
		||||
@ -147,8 +149,8 @@ func (s *HTTPProxy) Serve(c net.Conn) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *HTTPProxy) servHTTPS(method, requestURI, proto string, c net.Conn) {
 | 
			
		||||
	rc, err := s.GetProxy(requestURI).Dial("tcp", requestURI)
 | 
			
		||||
func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) {
 | 
			
		||||
	rc, err := s.sDialer.Dial("tcp", requestURI)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Write([]byte(proto))
 | 
			
		||||
		c.Write([]byte(" 502 ERROR\r\n\r\n"))
 | 
			
		||||
@ -170,8 +172,8 @@ func (s *HTTPProxy) servHTTPS(method, requestURI, proto string, c net.Conn) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial connects to the address addr on the network net via the proxy.
 | 
			
		||||
func (s *HTTPProxy) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	rc, err := s.GetProxy(s.addr).Dial("tcp", s.addr)
 | 
			
		||||
func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	rc, err := s.cDialer.Dial("tcp", s.addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("dial to %s error: %s", s.addr, err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										183
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								main.go
									
									
									
									
									
								
							@ -1,155 +1,30 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"github.com/nadoo/conflag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// VERSION .
 | 
			
		||||
const VERSION = "0.4.0"
 | 
			
		||||
 | 
			
		||||
var conf struct {
 | 
			
		||||
	Verbose       bool
 | 
			
		||||
	Strategy      string
 | 
			
		||||
	CheckWebSite  string
 | 
			
		||||
	CheckDuration int
 | 
			
		||||
	Listen        []string
 | 
			
		||||
	Forward       []string
 | 
			
		||||
	RuleFile      []string
 | 
			
		||||
	RulesDir      string
 | 
			
		||||
 | 
			
		||||
	DNS       string
 | 
			
		||||
	DNSServer []string
 | 
			
		||||
 | 
			
		||||
	IPSet string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var flag = conflag.New()
 | 
			
		||||
 | 
			
		||||
func logf(f string, v ...interface{}) {
 | 
			
		||||
	if conf.Verbose {
 | 
			
		||||
		log.Printf(f, v...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func usage() {
 | 
			
		||||
	app := os.Args[0]
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "%s v%s usage:\n", app, VERSION)
 | 
			
		||||
	flag.PrintDefaults()
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available Schemas:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  mixed: serve as a http/socks5 proxy on the same port. (default)\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  ss: ss proxy\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  socks5: socks5 proxy\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  http: http proxy\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  redir: redirect proxy. (used on linux as a transparent proxy with iptables redirect rules)\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  tcptun: a simple tcp tunnel\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  dnstun: listen on udp port and forward all dns requests to remote dns server via forwarders(tcp)\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available schemas for different modes:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  listen: mixed ss socks5 http redir tcptun dnstun\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  forward: ss socks5 http\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available methods for ss:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+ListCipher())
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  NOTE: chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Available forward strategies:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  rr: Round Robin mode\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  ha: High Availability mode\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Config file format(see `"+app+".conf.example` as an example):\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  # COMMENT LINE\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  KEY=VALUE\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  KEY=VALUE\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  # KEY equals to command line flag name: listen forward strategy...\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Examples:\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -config glider.conf\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -run glider with specified config file.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -config glider.conf -rulefile office.rule -rulefile home.rule\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -run glider with specified global config file and rule config files.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen :8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :8443, serve as http/socks5 proxy on the same port.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on 0.0.0.0:8443 as a ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen socks5://:1080 -verbose\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1080 as a socks5 proxy server, in verbose mode.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen http://:8080 -forward socks5://127.0.0.1:1080\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :8080 as a http proxy server, forward all requests via socks5 server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen redir://:1081 -forward ss://method:pass@1.1.1.1:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1081 as a transparent redirect server, forward all requests via remote ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen socks5://:1080 -listen http://:8080 -forward ss://method:pass@1.1.1.1:8443\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1080 as socks5 server, :8080 as http proxy server, forward all requests via remote ss server.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen redir://:1081 -listen dnstun://:53=8.8.8.8:53 -forward ss://method:pass@server1:port1,ss://method:pass@server2:port2\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1081 as transparent redirect server, :53 as dns server, use forward chain: server1 -> server2.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "  "+app+" -listen socks5://:1080 -forward ss://method:pass@server1:port1 -forward ss://method:pass@server2:port2 -strategy rr\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "    -listen on :1080 as socks5 server, forward requests via server1 and server2 in roundrbin mode.\n")
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
 | 
			
		||||
	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.CheckDuration, "checkduration", 30, "proxy check duration(seconds)")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.Listen, "listen", nil, "listen url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.Forward, "forward", nil, "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.RuleFile, "rulefile", nil, "rule file path")
 | 
			
		||||
	flag.StringVar(&conf.RulesDir, "rules-dir", "rules.d", "rule file folder")
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&conf.DNS, "dns", "", "dns listen address")
 | 
			
		||||
	flag.StringSliceUniqVar(&conf.DNSServer, "dnsserver", []string{"8.8.8.8:53"}, "remote dns server")
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&conf.IPSet, "ipset", "glider", "ipset name")
 | 
			
		||||
 | 
			
		||||
	flag.Usage = usage
 | 
			
		||||
	err := flag.Parse()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(conf.Listen) == 0 && conf.DNS == "" {
 | 
			
		||||
		flag.Usage()
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "ERROR: listen url must be specified.\n")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
func dialerFromConf() Dialer {
 | 
			
		||||
	// global forwarders in xx.conf
 | 
			
		||||
	var forwarders []Proxy
 | 
			
		||||
	var forwarders []Dialer
 | 
			
		||||
	for _, chain := range conf.Forward {
 | 
			
		||||
		var forward Proxy
 | 
			
		||||
		var forward Dialer
 | 
			
		||||
		var err error
 | 
			
		||||
		for _, url := range strings.Split(chain, ",") {
 | 
			
		||||
			forward, err = ProxyFromURL(url, forward)
 | 
			
		||||
			forward, err = DialerFromURL(url, forward)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
@ -157,36 +32,18 @@ func main() {
 | 
			
		||||
		forwarders = append(forwarders, forward)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// combine forwarders to a single strategy forwarder
 | 
			
		||||
	forwarder := NewStrategyForwarder(conf.Strategy, forwarders)
 | 
			
		||||
	forwarder := NewStrategyDialer(conf.Strategy, forwarders, conf.CheckWebSite, conf.CheckDuration)
 | 
			
		||||
 | 
			
		||||
	// rule forwarders
 | 
			
		||||
	var ruleForwarders []*RuleForwarder
 | 
			
		||||
	for _, ruleFile := range conf.RuleFile {
 | 
			
		||||
		ruleForwarder, err := NewRuleForwarderFromFile(ruleFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	return NewRuleDialer(conf.rules, forwarder)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		ruleForwarders = append(ruleForwarders, ruleForwarder)
 | 
			
		||||
	}
 | 
			
		||||
func main() {
 | 
			
		||||
 | 
			
		||||
	// rules folder
 | 
			
		||||
	ruleFolderFiles, _ := listDir(conf.RulesDir, ".rule")
 | 
			
		||||
	for _, ruleFile := range ruleFolderFiles {
 | 
			
		||||
		ruleForwarder, err := NewRuleForwarderFromFile(ruleFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ruleForwarders = append(ruleForwarders, ruleForwarder)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// combine ruleforwarders and global strategy forwarder
 | 
			
		||||
	forwarder = NewRulesForwarder(ruleForwarders, forwarder)
 | 
			
		||||
	confInit()
 | 
			
		||||
	sDialer := dialerFromConf()
 | 
			
		||||
 | 
			
		||||
	for _, listen := range conf.Listen {
 | 
			
		||||
		local, err := ProxyFromURL(listen, forwarder)
 | 
			
		||||
		local, err := ServerFromURL(listen, sDialer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
@ -194,20 +51,14 @@ func main() {
 | 
			
		||||
		go local.ListenAndServe()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(forwarders) > 1 {
 | 
			
		||||
		for _, forward := range forwarders {
 | 
			
		||||
			go check(forward, conf.CheckWebSite, conf.CheckDuration)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if conf.DNS != "" {
 | 
			
		||||
		dns, err := NewDNS(conf.DNS, conf.DNSServer[0], forwarder)
 | 
			
		||||
		dns, err := NewDNS(conf.DNS, conf.DNSServer[0], sDialer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// rule
 | 
			
		||||
		for _, frwder := range ruleForwarders {
 | 
			
		||||
		for _, frwder := range conf.rules {
 | 
			
		||||
			for _, domain := range frwder.Domain {
 | 
			
		||||
				if len(frwder.DNSServer) > 0 {
 | 
			
		||||
					dns.SetServer(domain, frwder.DNSServer[0])
 | 
			
		||||
@ -215,6 +66,14 @@ func main() {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// test here
 | 
			
		||||
		dns.AddAnswerHandler(func(domain, ip string) error {
 | 
			
		||||
			if ip != "" {
 | 
			
		||||
				logf("domain: %s, ip: %s\n", domain, ip)
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		go dns.ListenAndServe()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								mixed.go
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								mixed.go
									
									
									
									
									
								
							@ -19,23 +19,26 @@ var httpMethods = [...][]byte{
 | 
			
		||||
 | 
			
		||||
// MixedProxy .
 | 
			
		||||
type MixedProxy struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
	http   Proxy
 | 
			
		||||
	socks5 Proxy
 | 
			
		||||
	ss     Proxy
 | 
			
		||||
	sDialer Dialer
 | 
			
		||||
 | 
			
		||||
	addr   string
 | 
			
		||||
	http   *HTTP
 | 
			
		||||
	socks5 *SOCKS5
 | 
			
		||||
	ss     *SS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMixedProxy returns a mixed proxy.
 | 
			
		||||
func NewMixedProxy(network, addr, user, pass string, upProxy Proxy) (*MixedProxy, error) {
 | 
			
		||||
func NewMixedProxy(network, addr, user, pass string, sDialer Dialer) (*MixedProxy, error) {
 | 
			
		||||
	p := &MixedProxy{
 | 
			
		||||
		proxy: NewProxy(addr, upProxy),
 | 
			
		||||
		sDialer: sDialer,
 | 
			
		||||
		addr:    addr,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.http, _ = NewHTTPProxy(addr, upProxy)
 | 
			
		||||
	p.socks5, _ = NewSOCKS5Proxy(network, addr, user, pass, upProxy)
 | 
			
		||||
	p.http, _ = NewHTTP(addr, nil, sDialer)
 | 
			
		||||
	p.socks5, _ = NewSOCKS5(network, addr, user, pass, nil, sDialer)
 | 
			
		||||
 | 
			
		||||
	if user != "" && pass != "" {
 | 
			
		||||
		p.ss, _ = NewSSProxy(addr, user, pass, upProxy)
 | 
			
		||||
		p.ss, _ = NewSS(addr, user, pass, nil, sDialer)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p, nil
 | 
			
		||||
@ -58,48 +61,49 @@ func (p *MixedProxy) ListenAndServe() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer c.Close()
 | 
			
		||||
 | 
			
		||||
			if c, ok := c.(*net.TCPConn); ok {
 | 
			
		||||
				c.SetKeepAlive(true)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			c := newConn(c)
 | 
			
		||||
 | 
			
		||||
			if p.socks5 != nil {
 | 
			
		||||
				head, err := c.Peek(1)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					logf("peek error: %s", err)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// check socks5, client send socksversion: 5 as the first byte
 | 
			
		||||
				if head[0] == socks5Version {
 | 
			
		||||
					p.socks5.Serve(c)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if p.http != nil {
 | 
			
		||||
				head, err := c.Peek(8)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					logf("peek error: %s", err)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				for _, method := range httpMethods {
 | 
			
		||||
					if bytes.HasPrefix(head, method) {
 | 
			
		||||
						p.http.Serve(c)
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if p.ss != nil {
 | 
			
		||||
				p.ss.Serve(c)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}()
 | 
			
		||||
		go p.Serve(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *MixedProxy) Serve(conn net.Conn) {
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	if c, ok := conn.(*net.TCPConn); ok {
 | 
			
		||||
		c.SetKeepAlive(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := newConn(conn)
 | 
			
		||||
 | 
			
		||||
	if p.socks5 != nil {
 | 
			
		||||
		head, err := c.Peek(1)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logf("peek error: %s", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// check socks5, client send socksversion: 5 as the first byte
 | 
			
		||||
		if head[0] == socks5Version {
 | 
			
		||||
			p.socks5.Serve(c)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.http != nil {
 | 
			
		||||
		head, err := c.Peek(8)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logf("peek error: %s", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, method := range httpMethods {
 | 
			
		||||
			if bytes.HasPrefix(head, method) {
 | 
			
		||||
				p.http.Serve(c)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.ss != nil {
 | 
			
		||||
		p.ss.Serve(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										159
									
								
								proxy.go
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								proxy.go
									
									
									
									
									
								
							@ -1,159 +0,0 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Proxy means to establish a connection and relay it.
 | 
			
		||||
type Proxy interface {
 | 
			
		||||
	// Get address
 | 
			
		||||
	Addr() string
 | 
			
		||||
 | 
			
		||||
	// ListenAndServe as proxy server, use only in server mode.
 | 
			
		||||
	ListenAndServe()
 | 
			
		||||
 | 
			
		||||
	// Serve as proxy server, use only in server mode.
 | 
			
		||||
	Serve(c net.Conn)
 | 
			
		||||
 | 
			
		||||
	// Get current proxy
 | 
			
		||||
	CurrentProxy() Proxy
 | 
			
		||||
 | 
			
		||||
	// Get a proxy based on the destAddr and strategy
 | 
			
		||||
	GetProxy(dstAddr string) Proxy
 | 
			
		||||
 | 
			
		||||
	// Switch to the next proxy
 | 
			
		||||
	NextProxy() Proxy
 | 
			
		||||
 | 
			
		||||
	// Get the status of proxy
 | 
			
		||||
	Enabled() bool
 | 
			
		||||
 | 
			
		||||
	// Set the proxy status
 | 
			
		||||
	SetEnable(enable bool)
 | 
			
		||||
 | 
			
		||||
	// Dial connects to the given address via the proxy.
 | 
			
		||||
	Dial(network, addr string) (c net.Conn, err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// proxy
 | 
			
		||||
type proxy struct {
 | 
			
		||||
	addr    string
 | 
			
		||||
	forward Proxy
 | 
			
		||||
	enabled bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewProxy .
 | 
			
		||||
func NewProxy(addr string, forward Proxy) *proxy {
 | 
			
		||||
	if forward == nil {
 | 
			
		||||
		forward = Direct
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &proxy{addr: addr, forward: forward, enabled: true}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *proxy) Addr() string                  { return p.addr }
 | 
			
		||||
func (p *proxy) ListenAndServe()               { logf("base proxy ListenAndServe") }
 | 
			
		||||
func (p *proxy) Serve(c net.Conn)              { logf("base proxy Serve") }
 | 
			
		||||
func (p *proxy) CurrentProxy() Proxy           { return p.forward }
 | 
			
		||||
func (p *proxy) GetProxy(dstAddr string) Proxy { return p.forward }
 | 
			
		||||
func (p *proxy) NextProxy() Proxy              { return p.forward }
 | 
			
		||||
func (p *proxy) Enabled() bool                 { return p.enabled }
 | 
			
		||||
func (p *proxy) SetEnable(enable bool)         { p.enabled = enable }
 | 
			
		||||
 | 
			
		||||
func (p *proxy) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return p.forward.Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProxyFromURL parses url and get a Proxy
 | 
			
		||||
// TODO: table
 | 
			
		||||
func ProxyFromURL(s string, forwarder Proxy) (Proxy, error) {
 | 
			
		||||
	if !strings.Contains(s, "://") {
 | 
			
		||||
		s = "mixed://" + s
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u, err := url.Parse(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("parse err: %s", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addr := u.Host
 | 
			
		||||
	var user, pass string
 | 
			
		||||
	if u.User != nil {
 | 
			
		||||
		user = u.User.Username()
 | 
			
		||||
		pass, _ = u.User.Password()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch u.Scheme {
 | 
			
		||||
	case "mixed":
 | 
			
		||||
		return NewMixedProxy("tcp", addr, user, pass, forwarder)
 | 
			
		||||
	case "http":
 | 
			
		||||
		return NewHTTPProxy(addr, forwarder)
 | 
			
		||||
	case "socks5":
 | 
			
		||||
		return NewSOCKS5Proxy("tcp", addr, user, pass, forwarder)
 | 
			
		||||
	case "ss":
 | 
			
		||||
		p, err := NewSSProxy(addr, user, pass, forwarder)
 | 
			
		||||
		return p, err
 | 
			
		||||
	case "redir":
 | 
			
		||||
		return NewRedirProxy(addr, forwarder)
 | 
			
		||||
	case "tcptun":
 | 
			
		||||
		d := strings.Split(addr, "=")
 | 
			
		||||
		return NewTCPTun(d[0], d[1], forwarder)
 | 
			
		||||
	case "dnstun":
 | 
			
		||||
		d := strings.Split(addr, "=")
 | 
			
		||||
		return NewDNSTun(d[0], d[1], forwarder)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errors.New("unknown schema '" + u.Scheme + "'")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check proxy
 | 
			
		||||
func check(p Proxy, webhost string, duration int) {
 | 
			
		||||
	retry := 1
 | 
			
		||||
	buf := make([]byte, 4)
 | 
			
		||||
 | 
			
		||||
	if strings.IndexByte(webhost, ':') == -1 {
 | 
			
		||||
		webhost = webhost + ":80"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		time.Sleep(time.Duration(duration) * time.Second * time.Duration(retry>>1))
 | 
			
		||||
		retry <<= 1
 | 
			
		||||
 | 
			
		||||
		if retry > 16 {
 | 
			
		||||
			retry = 16
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		startTime := time.Now()
 | 
			
		||||
		c, err := p.Dial("tcp", webhost)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			p.SetEnable(false)
 | 
			
		||||
			logf("proxy-check %s -> %s, set to DISABLED. error in dial: %s", p.Addr(), webhost, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Write([]byte("GET / HTTP/1.0"))
 | 
			
		||||
		c.Write([]byte("\r\n\r\n"))
 | 
			
		||||
 | 
			
		||||
		_, err = io.ReadFull(c, buf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			p.SetEnable(false)
 | 
			
		||||
			logf("proxy-check %s -> %s, set to DISABLED. error in read: %s", p.Addr(), webhost, err)
 | 
			
		||||
		} else if bytes.Equal([]byte("HTTP"), buf) {
 | 
			
		||||
			p.SetEnable(true)
 | 
			
		||||
			retry = 2
 | 
			
		||||
			dialTime := time.Since(startTime)
 | 
			
		||||
			logf("proxy-check %s -> %s, set to ENABLED. connect time: %s", p.Addr(), webhost, dialTime.String())
 | 
			
		||||
		} else {
 | 
			
		||||
			p.SetEnable(false)
 | 
			
		||||
			logf("proxy-check %s -> %s, set to DISABLED. server response: %s", p.Addr(), webhost, buf)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -18,13 +18,15 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RedirProxy struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
	*Forwarder        // as client
 | 
			
		||||
	sDialer    Dialer // dialer for server
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRedirProxy returns a redirect proxy.
 | 
			
		||||
func NewRedirProxy(addr string, upProxy Proxy) (*RedirProxy, error) {
 | 
			
		||||
func NewRedirProxy(addr string, sDialer Dialer) (*RedirProxy, error) {
 | 
			
		||||
	s := &RedirProxy{
 | 
			
		||||
		proxy: NewProxy(addr, upProxy),
 | 
			
		||||
		Forwarder: NewForwarder(addr, nil),
 | 
			
		||||
		sDialer:   sDialer,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
@ -60,7 +62,7 @@ func (s *RedirProxy) ListenAndServe() {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			rc, err := s.GetProxy(tgt.String()).Dial("tcp", tgt.String())
 | 
			
		||||
			rc, err := s.sDialer.Dial("tcp", tgt.String())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logf("failed to connect to target: %v", err)
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
@ -2,14 +2,17 @@
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "log"
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RedirProxy .
 | 
			
		||||
type RedirProxy struct{ *proxy }
 | 
			
		||||
type RedirProxy struct{}
 | 
			
		||||
 | 
			
		||||
// NewRedirProxy returns a redirect proxy.
 | 
			
		||||
func NewRedirProxy(addr string, upProxy Proxy) (Proxy, error) {
 | 
			
		||||
	return &RedirProxy{proxy: NewProxy(addr, upProxy)}, nil
 | 
			
		||||
func NewRedirProxy(addr string, sDialer Dialer) (*RedirProxy, error) {
 | 
			
		||||
	return nil, errors.New("redir not supported on this os")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenAndServe redirected requests as a server.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										150
									
								
								rule.go
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								rule.go
									
									
									
									
									
								
							@ -1,75 +1,119 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/nadoo/conflag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RuleForwarder , every ruleForwarder points to a rule file
 | 
			
		||||
type RuleForwarder struct {
 | 
			
		||||
	Forward       []string
 | 
			
		||||
	Strategy      string
 | 
			
		||||
	CheckWebSite  string
 | 
			
		||||
	CheckDuration int
 | 
			
		||||
// RuleDialer .
 | 
			
		||||
type RuleDialer struct {
 | 
			
		||||
	gDialer Dialer
 | 
			
		||||
 | 
			
		||||
	DNSServer []string
 | 
			
		||||
	IPSet     string
 | 
			
		||||
 | 
			
		||||
	Domain []string
 | 
			
		||||
	IP     []string
 | 
			
		||||
	CIDR   []string
 | 
			
		||||
 | 
			
		||||
	name string
 | 
			
		||||
	Proxy
 | 
			
		||||
	domainMap map[string]Dialer
 | 
			
		||||
	ipMap     map[string]Dialer
 | 
			
		||||
	cidrMap   map[string]Dialer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRuleForwarderFromFile .
 | 
			
		||||
func NewRuleForwarderFromFile(ruleFile string) (*RuleForwarder, error) {
 | 
			
		||||
	p := &RuleForwarder{name: ruleFile}
 | 
			
		||||
// NewRuleDialer .
 | 
			
		||||
func NewRuleDialer(rules []*RuleConf, gDialer Dialer) Dialer {
 | 
			
		||||
 | 
			
		||||
	f := conflag.NewFromFile("rule", ruleFile)
 | 
			
		||||
	f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
 | 
			
		||||
	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.CheckDuration, "checkduration", 30, "proxy check duration(seconds)")
 | 
			
		||||
 | 
			
		||||
	f.StringSliceUniqVar(&p.DNSServer, "dnsserver", nil, "remote dns server")
 | 
			
		||||
	f.StringVar(&p.IPSet, "ipset", "", "ipset name")
 | 
			
		||||
 | 
			
		||||
	f.StringSliceUniqVar(&p.Domain, "domain", nil, "domain")
 | 
			
		||||
	f.StringSliceUniqVar(&p.IP, "ip", nil, "ip")
 | 
			
		||||
	f.StringSliceUniqVar(&p.CIDR, "cidr", nil, "cidr")
 | 
			
		||||
 | 
			
		||||
	err := f.Parse()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	if len(rules) == 0 {
 | 
			
		||||
		return gDialer
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var forwarders []Proxy
 | 
			
		||||
	for _, chain := range p.Forward {
 | 
			
		||||
		var forward Proxy
 | 
			
		||||
		var err error
 | 
			
		||||
		for _, url := range strings.Split(chain, ",") {
 | 
			
		||||
			forward, err = ProxyFromURL(url, forward)
 | 
			
		||||
	rd := &RuleDialer{gDialer: gDialer}
 | 
			
		||||
 | 
			
		||||
	for _, r := range rules {
 | 
			
		||||
		var forwarders []Dialer
 | 
			
		||||
		for _, chain := range r.Forward {
 | 
			
		||||
			var forward Dialer
 | 
			
		||||
			var err error
 | 
			
		||||
			for _, url := range strings.Split(chain, ",") {
 | 
			
		||||
				forward, err = DialerFromURL(url, forward)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			forwarders = append(forwarders, forward)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sd := NewStrategyDialer(r.Strategy, forwarders, r.CheckWebSite, r.CheckDuration)
 | 
			
		||||
 | 
			
		||||
		rd.domainMap = make(map[string]Dialer)
 | 
			
		||||
		for _, domain := range r.Domain {
 | 
			
		||||
			rd.domainMap[domain] = sd
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rd.ipMap = make(map[string]Dialer)
 | 
			
		||||
		for _, ip := range r.IP {
 | 
			
		||||
			rd.ipMap[ip] = sd
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// dnsserver should use rule forwarder too
 | 
			
		||||
		for _, dnss := range r.DNSServer {
 | 
			
		||||
			ip, _, err := net.SplitHostPort(dnss)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
				logf("SplitHostPort ERROR: %s", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			rd.ipMap[ip] = sd
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rd.cidrMap = make(map[string]Dialer)
 | 
			
		||||
		for _, cidr := range r.CIDR {
 | 
			
		||||
			rd.cidrMap[cidr] = sd
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rd *RuleDialer) Addr() string { return "RULES" }
 | 
			
		||||
 | 
			
		||||
func (p *RuleDialer) NextDialer(dstAddr string) Dialer {
 | 
			
		||||
 | 
			
		||||
	// TODO: change to index finders
 | 
			
		||||
	host, _, err := net.SplitHostPort(dstAddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// TODO: check here
 | 
			
		||||
		logf("SplitHostPort ERROR: %s", err)
 | 
			
		||||
		return p.gDialer
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// find ip
 | 
			
		||||
	if ip := net.ParseIP(host); ip != nil {
 | 
			
		||||
		// check ip
 | 
			
		||||
		if d, ok := p.ipMap[ip.String()]; ok {
 | 
			
		||||
			return d
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// check cidr
 | 
			
		||||
		// TODO: do not parse cidr every time
 | 
			
		||||
		for cidrStr, d := range p.cidrMap {
 | 
			
		||||
			if _, net, err := net.ParseCIDR(cidrStr); err == nil {
 | 
			
		||||
				if net.Contains(ip) {
 | 
			
		||||
					return d
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		forwarders = append(forwarders, forward)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	forwarder := NewStrategyForwarder(p.Strategy, forwarders)
 | 
			
		||||
	domainParts := strings.Split(host, ".")
 | 
			
		||||
	length := len(domainParts)
 | 
			
		||||
	for i := length - 2; i >= 0; i-- {
 | 
			
		||||
		domain := strings.Join(domainParts[i:length], ".")
 | 
			
		||||
 | 
			
		||||
	for _, forward := range forwarders {
 | 
			
		||||
		go check(forward, p.CheckWebSite, p.CheckDuration)
 | 
			
		||||
		// find in domainMap
 | 
			
		||||
		if d, ok := p.domainMap[domain]; ok {
 | 
			
		||||
			return d
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.Proxy = forwarder
 | 
			
		||||
 | 
			
		||||
	return p, err
 | 
			
		||||
	return p.gDialer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rd *RuleDialer) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	d := rd.NextDialer(addr)
 | 
			
		||||
	return d.Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										102
									
								
								rules.go
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								rules.go
									
									
									
									
									
								
							@ -1,102 +0,0 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RulesForwarder .
 | 
			
		||||
type RulesForwarder struct {
 | 
			
		||||
	globalForwarder Proxy
 | 
			
		||||
 | 
			
		||||
	domainMap map[string]Proxy
 | 
			
		||||
	ipMap     map[string]Proxy
 | 
			
		||||
	cidrMap   map[string]Proxy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRulesForwarder .
 | 
			
		||||
func NewRulesForwarder(ruleForwarders []*RuleForwarder, globalForwarder Proxy) Proxy {
 | 
			
		||||
 | 
			
		||||
	if len(ruleForwarders) == 0 {
 | 
			
		||||
		return globalForwarder
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := &RulesForwarder{globalForwarder: globalForwarder}
 | 
			
		||||
 | 
			
		||||
	for _, f := range ruleForwarders {
 | 
			
		||||
		p.domainMap = make(map[string]Proxy)
 | 
			
		||||
		for _, domain := range f.Domain {
 | 
			
		||||
			p.domainMap[domain] = f.Proxy
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		p.ipMap = make(map[string]Proxy)
 | 
			
		||||
		for _, ip := range f.IP {
 | 
			
		||||
			p.ipMap[ip] = f.Proxy
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		p.cidrMap = make(map[string]Proxy)
 | 
			
		||||
		for _, cidr := range f.CIDR {
 | 
			
		||||
			p.cidrMap[cidr] = f.Proxy
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *RulesForwarder) Addr() string        { return "rules forwarder" }
 | 
			
		||||
func (p *RulesForwarder) ListenAndServe()     {}
 | 
			
		||||
func (p *RulesForwarder) Serve(c net.Conn)    {}
 | 
			
		||||
func (p *RulesForwarder) CurrentProxy() Proxy { return p.globalForwarder.CurrentProxy() }
 | 
			
		||||
 | 
			
		||||
func (p *RulesForwarder) GetProxy(dstAddr string) Proxy {
 | 
			
		||||
 | 
			
		||||
	// TODO: change to index finders
 | 
			
		||||
	host, _, err := net.SplitHostPort(dstAddr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// TODO: check here
 | 
			
		||||
		logf("SplitHostPort ERROR: %s", err)
 | 
			
		||||
		return p.globalForwarder.GetProxy(dstAddr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// find ip
 | 
			
		||||
	if ip := net.ParseIP(host); ip != nil {
 | 
			
		||||
		// check ip
 | 
			
		||||
		if proxy, ok := p.ipMap[ip.String()]; ok {
 | 
			
		||||
			return proxy
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// check cidr
 | 
			
		||||
		// TODO: do not parse cidr every time
 | 
			
		||||
		for cidrStr, proxy := range p.cidrMap {
 | 
			
		||||
			if _, net, err := net.ParseCIDR(cidrStr); err == nil {
 | 
			
		||||
				if net.Contains(ip) {
 | 
			
		||||
					return proxy
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	domainParts := strings.Split(host, ".")
 | 
			
		||||
	length := len(domainParts)
 | 
			
		||||
	for i := length - 2; i >= 0; i-- {
 | 
			
		||||
		domain := strings.Join(domainParts[i:length], ".")
 | 
			
		||||
 | 
			
		||||
		// find in domainMap
 | 
			
		||||
		if proxy, ok := p.domainMap[domain]; ok {
 | 
			
		||||
			return proxy
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p.globalForwarder.GetProxy(dstAddr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *RulesForwarder) NextProxy() Proxy {
 | 
			
		||||
	return p.globalForwarder.NextProxy()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *RulesForwarder) Enabled() bool         { return true }
 | 
			
		||||
func (p *RulesForwarder) SetEnable(enable bool) {}
 | 
			
		||||
 | 
			
		||||
func (p *RulesForwarder) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return p.GetProxy(addr).Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								server.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Server .
 | 
			
		||||
type Server interface {
 | 
			
		||||
	// ListenAndServe as proxy server, use only in server mode.
 | 
			
		||||
	ListenAndServe()
 | 
			
		||||
 | 
			
		||||
	// Serve
 | 
			
		||||
	// Serve(c net.Conn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerFromURL parses url and get a Proxy
 | 
			
		||||
// TODO: table
 | 
			
		||||
func ServerFromURL(s string, sDialer Dialer) (Server, error) {
 | 
			
		||||
	if !strings.Contains(s, "://") {
 | 
			
		||||
		s = "mixed://" + s
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u, err := url.Parse(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("parse err: %s", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addr := u.Host
 | 
			
		||||
	var user, pass string
 | 
			
		||||
	if u.User != nil {
 | 
			
		||||
		user = u.User.Username()
 | 
			
		||||
		pass, _ = u.User.Password()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if sDialer == nil {
 | 
			
		||||
		sDialer = Direct
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch u.Scheme {
 | 
			
		||||
	case "mixed":
 | 
			
		||||
		return NewMixedProxy("tcp", addr, user, pass, sDialer)
 | 
			
		||||
	case "http":
 | 
			
		||||
		return NewHTTP(addr, nil, sDialer)
 | 
			
		||||
	case "socks5":
 | 
			
		||||
		return NewSOCKS5("tcp", addr, user, pass, nil, sDialer)
 | 
			
		||||
	case "ss":
 | 
			
		||||
		p, err := NewSS(addr, user, pass, nil, sDialer)
 | 
			
		||||
		return p, err
 | 
			
		||||
	case "redir":
 | 
			
		||||
		return NewRedirProxy(addr, sDialer)
 | 
			
		||||
	case "tcptun":
 | 
			
		||||
		d := strings.Split(addr, "=")
 | 
			
		||||
		return NewTCPTun(d[0], d[1], sDialer)
 | 
			
		||||
	case "dnstun":
 | 
			
		||||
		d := strings.Split(addr, "=")
 | 
			
		||||
		return NewDNSTun(d[0], d[1], sDialer)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errors.New("unknown schema '" + u.Scheme + "'")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								socks5.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								socks5.go
									
									
									
									
									
								
							@ -55,28 +55,31 @@ var socks5Errors = []string{
 | 
			
		||||
	"address type not supported",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SOCKS5Proxy .
 | 
			
		||||
type SOCKS5Proxy struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
// SOCKS5 .
 | 
			
		||||
type SOCKS5 struct {
 | 
			
		||||
	*Forwarder
 | 
			
		||||
	sDialer Dialer
 | 
			
		||||
 | 
			
		||||
	network  string
 | 
			
		||||
	user     string
 | 
			
		||||
	password string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSOCKS5Proxy returns a Proxy that makes SOCKSv5 connections to the given address
 | 
			
		||||
// NewSOCKS5 returns a Proxy that makes SOCKSv5 connections to the given address
 | 
			
		||||
// with an optional username and password. See RFC 1928.
 | 
			
		||||
func NewSOCKS5Proxy(network, addr, user, pass string, upProxy Proxy) (*SOCKS5Proxy, error) {
 | 
			
		||||
	s := &SOCKS5Proxy{
 | 
			
		||||
		proxy:    NewProxy(addr, upProxy),
 | 
			
		||||
		user:     user,
 | 
			
		||||
		password: pass,
 | 
			
		||||
func NewSOCKS5(network, addr, user, pass string, cDialer Dialer, sDialer Dialer) (*SOCKS5, error) {
 | 
			
		||||
	s := &SOCKS5{
 | 
			
		||||
		Forwarder: NewForwarder(addr, cDialer),
 | 
			
		||||
		sDialer:   sDialer,
 | 
			
		||||
		user:      user,
 | 
			
		||||
		password:  pass,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenAndServe connects to the address addr on the network net via the SOCKS5 proxy.
 | 
			
		||||
func (s *SOCKS5Proxy) ListenAndServe() {
 | 
			
		||||
func (s *SOCKS5) ListenAndServe() {
 | 
			
		||||
	l, err := net.Listen("tcp", s.addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("failed to listen on %s: %v", s.addr, err)
 | 
			
		||||
@ -97,7 +100,7 @@ func (s *SOCKS5Proxy) ListenAndServe() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serve .
 | 
			
		||||
func (s *SOCKS5Proxy) Serve(c net.Conn) {
 | 
			
		||||
func (s *SOCKS5) Serve(c net.Conn) {
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
 | 
			
		||||
	if c, ok := c.(*net.TCPConn); ok {
 | 
			
		||||
@ -110,7 +113,7 @@ func (s *SOCKS5Proxy) Serve(c net.Conn) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc, err := s.GetProxy(tgt.String()).Dial("tcp", tgt.String())
 | 
			
		||||
	rc, err := s.sDialer.Dial("tcp", tgt.String())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("failed to connect to target: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
@ -129,14 +132,14 @@ func (s *SOCKS5Proxy) Serve(c net.Conn) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial connects to the address addr on the network net via the SOCKS5 proxy.
 | 
			
		||||
func (s *SOCKS5Proxy) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	switch network {
 | 
			
		||||
	case "tcp", "tcp6", "tcp4":
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := s.GetProxy(s.addr).Dial(s.network, s.addr)
 | 
			
		||||
	c, err := s.cDialer.Dial(s.network, s.addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("dial to %s error: %s", s.addr, err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
@ -157,7 +160,7 @@ func (s *SOCKS5Proxy) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
// 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 *SOCKS5Proxy) connect(conn net.Conn, target string) error {
 | 
			
		||||
func (s *SOCKS5) connect(conn net.Conn, target string) error {
 | 
			
		||||
	host, portStr, err := net.SplitHostPort(target)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@ -288,7 +291,7 @@ func (s *SOCKS5Proxy) connect(conn net.Conn, target string) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Handshake fast-tracks SOCKS initialization to get target address to connect.
 | 
			
		||||
func (s *SOCKS5Proxy) handshake(rw io.ReadWriter) (Addr, error) {
 | 
			
		||||
func (s *SOCKS5) handshake(rw io.ReadWriter) (Addr, error) {
 | 
			
		||||
	// Read RFC 1928 for request and reply structure and sizes.
 | 
			
		||||
	buf := make([]byte, MaxAddrLen)
 | 
			
		||||
	// read VER, NMETHODS, METHODS
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								ss.go
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								ss.go
									
									
									
									
									
								
							@ -9,21 +9,24 @@ import (
 | 
			
		||||
	"github.com/shadowsocks/go-shadowsocks2/core"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SSProxy .
 | 
			
		||||
type SSProxy struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
// SS .
 | 
			
		||||
type SS struct {
 | 
			
		||||
	*Forwarder
 | 
			
		||||
	sDialer Dialer
 | 
			
		||||
 | 
			
		||||
	core.StreamConnCipher
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSSProxy returns a shadowsocks proxy.
 | 
			
		||||
func NewSSProxy(addr, method, pass string, upProxy Proxy) (*SSProxy, error) {
 | 
			
		||||
// NewSS returns a shadowsocks proxy.
 | 
			
		||||
func NewSS(addr, method, pass string, cDialer Dialer, sDialer Dialer) (*SS, error) {
 | 
			
		||||
	ciph, err := core.PickCipher(method, nil, pass)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("PickCipher for '%s', error: %s", method, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := &SSProxy{
 | 
			
		||||
		proxy:            NewProxy(addr, upProxy),
 | 
			
		||||
	s := &SS{
 | 
			
		||||
		Forwarder:        NewForwarder(addr, cDialer),
 | 
			
		||||
		sDialer:          sDialer,
 | 
			
		||||
		StreamConnCipher: ciph,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -31,7 +34,7 @@ func NewSSProxy(addr, method, pass string, upProxy Proxy) (*SSProxy, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListenAndServe shadowsocks requests as a server.
 | 
			
		||||
func (s *SSProxy) ListenAndServe() {
 | 
			
		||||
func (s *SS) ListenAndServe() {
 | 
			
		||||
	l, err := net.Listen("tcp", s.addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("failed to listen on %s: %v", s.addr, err)
 | 
			
		||||
@ -51,7 +54,7 @@ func (s *SSProxy) ListenAndServe() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serve .
 | 
			
		||||
func (s *SSProxy) Serve(c net.Conn) {
 | 
			
		||||
func (s *SS) Serve(c net.Conn) {
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
 | 
			
		||||
	if c, ok := c.(*net.TCPConn); ok {
 | 
			
		||||
@ -66,7 +69,7 @@ func (s *SSProxy) Serve(c net.Conn) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc, err := s.GetProxy(tgt.String()).Dial("tcp", tgt.String())
 | 
			
		||||
	rc, err := s.sDialer.Dial("tcp", tgt.String())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("failed to connect to target: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
@ -86,14 +89,14 @@ func (s *SSProxy) Serve(c net.Conn) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial connects to the address addr on the network net via the proxy.
 | 
			
		||||
func (s *SSProxy) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
func (s *SS) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
 | 
			
		||||
	target := ParseAddr(addr)
 | 
			
		||||
	if target == nil {
 | 
			
		||||
		return nil, errors.New("Unable to parse address: " + addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c, err := s.GetProxy(s.addr).Dial("tcp", s.addr)
 | 
			
		||||
	c, err := s.cDialer.Dial("tcp", s.addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logf("dial to %s error: %s", s.addr, err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										159
									
								
								strategy.go
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								strategy.go
									
									
									
									
									
								
							@ -1,64 +1,80 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "net"
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewStrategyForwarder .
 | 
			
		||||
func NewStrategyForwarder(strategy string, forwarders []Proxy) Proxy {
 | 
			
		||||
	var proxy Proxy
 | 
			
		||||
	if len(forwarders) == 0 {
 | 
			
		||||
		proxy = Direct
 | 
			
		||||
	} else if len(forwarders) == 1 {
 | 
			
		||||
		proxy = forwarders[0]
 | 
			
		||||
	} else if len(forwarders) > 1 {
 | 
			
		||||
// NewStrategyDialer .
 | 
			
		||||
func NewStrategyDialer(strategy string, dialers []Dialer, website string, duration int) Dialer {
 | 
			
		||||
	var dialer Dialer
 | 
			
		||||
	if len(dialers) == 0 {
 | 
			
		||||
		dialer = Direct
 | 
			
		||||
	} else if len(dialers) == 1 {
 | 
			
		||||
		dialer = dialers[0]
 | 
			
		||||
	} else if len(dialers) > 1 {
 | 
			
		||||
		switch strategy {
 | 
			
		||||
		case "rr":
 | 
			
		||||
			proxy = newRRProxy("", forwarders)
 | 
			
		||||
			dialer = newRRDialer(dialers, website, duration)
 | 
			
		||||
			logf("forward to remote servers in round robin mode.")
 | 
			
		||||
		case "ha":
 | 
			
		||||
			proxy = newHAProxy("", forwarders)
 | 
			
		||||
			dialer = newHADialer(dialers, website, duration)
 | 
			
		||||
			logf("forward to remote servers in high availability mode.")
 | 
			
		||||
		default:
 | 
			
		||||
			logf("not supported forward mode '%s', just use the first forward server.", conf.Strategy)
 | 
			
		||||
			proxy = forwarders[0]
 | 
			
		||||
			dialer = dialers[0]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return proxy
 | 
			
		||||
	return dialer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rrProxy
 | 
			
		||||
type rrProxy struct {
 | 
			
		||||
	forwarders []Proxy
 | 
			
		||||
	idx        int
 | 
			
		||||
// rrDialer
 | 
			
		||||
type rrDialer struct {
 | 
			
		||||
	dialers []Dialer
 | 
			
		||||
	idx     int
 | 
			
		||||
 | 
			
		||||
	status map[int]bool
 | 
			
		||||
 | 
			
		||||
	// for checking
 | 
			
		||||
	website  string
 | 
			
		||||
	duration int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newRRProxy .
 | 
			
		||||
func newRRProxy(addr string, forwarders []Proxy) Proxy {
 | 
			
		||||
	if len(forwarders) == 0 {
 | 
			
		||||
		return Direct
 | 
			
		||||
	} else if len(forwarders) == 1 {
 | 
			
		||||
		return NewProxy(addr, forwarders[0])
 | 
			
		||||
// newRRDialer .
 | 
			
		||||
func newRRDialer(dialers []Dialer, website string, duration int) *rrDialer {
 | 
			
		||||
	rr := &rrDialer{dialers: dialers}
 | 
			
		||||
 | 
			
		||||
	rr.status = make(map[int]bool)
 | 
			
		||||
	rr.website = website
 | 
			
		||||
	rr.duration = duration
 | 
			
		||||
 | 
			
		||||
	for k := range dialers {
 | 
			
		||||
		rr.status[k] = true
 | 
			
		||||
		go rr.checkDialer(k)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &rrProxy{forwarders: forwarders}
 | 
			
		||||
	return rr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *rrProxy) Addr() string                  { return "strategy forwarder" }
 | 
			
		||||
func (p *rrProxy) ListenAndServe()               {}
 | 
			
		||||
func (p *rrProxy) Serve(c net.Conn)              {}
 | 
			
		||||
func (p *rrProxy) CurrentProxy() Proxy           { return p.forwarders[p.idx] }
 | 
			
		||||
func (p *rrProxy) GetProxy(dstAddr string) Proxy { return p.NextProxy() }
 | 
			
		||||
func (rr *rrDialer) Addr() string { return "STRATEGY" }
 | 
			
		||||
func (rr *rrDialer) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return rr.NextDialer().Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *rrProxy) NextProxy() Proxy {
 | 
			
		||||
	n := len(p.forwarders)
 | 
			
		||||
func (rr *rrDialer) NextDialer() Dialer {
 | 
			
		||||
	n := len(rr.dialers)
 | 
			
		||||
	if n == 1 {
 | 
			
		||||
		return p.forwarders[0]
 | 
			
		||||
		rr.idx = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	found := false
 | 
			
		||||
	for i := 0; i < n; i++ {
 | 
			
		||||
		p.idx = (p.idx + 1) % n
 | 
			
		||||
		if p.forwarders[p.idx].Enabled() {
 | 
			
		||||
		rr.idx = (rr.idx + 1) % n
 | 
			
		||||
		if rr.status[rr.idx] {
 | 
			
		||||
			found = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
@ -68,34 +84,73 @@ func (p *rrProxy) NextProxy() Proxy {
 | 
			
		||||
		logf("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p.forwarders[p.idx]
 | 
			
		||||
	return rr.dialers[rr.idx]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *rrProxy) Enabled() bool         { return true }
 | 
			
		||||
func (p *rrProxy) SetEnable(enable bool) {}
 | 
			
		||||
// Check dialer
 | 
			
		||||
func (rr *rrDialer) checkDialer(idx int) {
 | 
			
		||||
	retry := 1
 | 
			
		||||
	buf := make([]byte, 4)
 | 
			
		||||
 | 
			
		||||
func (p *rrProxy) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return p.GetProxy(addr).Dial(network, addr)
 | 
			
		||||
	if strings.IndexByte(rr.website, ':') == -1 {
 | 
			
		||||
		rr.website = rr.website + ":80"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d := rr.dialers[idx]
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		time.Sleep(time.Duration(rr.duration) * time.Second * time.Duration(retry>>1))
 | 
			
		||||
		retry <<= 1
 | 
			
		||||
 | 
			
		||||
		if retry > 16 {
 | 
			
		||||
			retry = 16
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		startTime := time.Now()
 | 
			
		||||
		c, err := d.Dial("tcp", rr.website)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rr.status[idx] = false
 | 
			
		||||
			logf("proxy-check %s -> %s, set to DISABLED. error in dial: %s", d.Addr(), rr.website, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Write([]byte("GET / HTTP/1.0"))
 | 
			
		||||
		c.Write([]byte("\r\n\r\n"))
 | 
			
		||||
 | 
			
		||||
		_, err = io.ReadFull(c, buf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rr.status[idx] = false
 | 
			
		||||
			logf("proxy-check %s -> %s, set to DISABLED. error in read: %s", d.Addr(), rr.website, err)
 | 
			
		||||
		} else if bytes.Equal([]byte("HTTP"), buf) {
 | 
			
		||||
			rr.status[idx] = true
 | 
			
		||||
			retry = 2
 | 
			
		||||
			dialTime := time.Since(startTime)
 | 
			
		||||
			logf("proxy-check %s -> %s, set to ENABLED. connect time: %s", d.Addr(), rr.website, dialTime.String())
 | 
			
		||||
		} else {
 | 
			
		||||
			rr.status[idx] = false
 | 
			
		||||
			logf("proxy-check %s -> %s, set to DISABLED. server response: %s", d.Addr(), rr.website, buf)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// high availability proxy
 | 
			
		||||
type haProxy struct {
 | 
			
		||||
	Proxy
 | 
			
		||||
type haDialer struct {
 | 
			
		||||
	*rrDialer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newHAProxy .
 | 
			
		||||
func newHAProxy(addr string, forwarders []Proxy) Proxy {
 | 
			
		||||
	return &haProxy{Proxy: newRRProxy(addr, forwarders)}
 | 
			
		||||
// newHADialer .
 | 
			
		||||
func newHADialer(dialers []Dialer, webhost string, duration int) Dialer {
 | 
			
		||||
	return &haDialer{rrDialer: newRRDialer(dialers, webhost, duration)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *haProxy) GetProxy(dstAddr string) Proxy {
 | 
			
		||||
	proxy := p.CurrentProxy()
 | 
			
		||||
	if proxy.Enabled() == false {
 | 
			
		||||
		return p.NextProxy()
 | 
			
		||||
func (ha *haDialer) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	d := ha.dialers[ha.idx]
 | 
			
		||||
 | 
			
		||||
	if !ha.status[ha.idx] {
 | 
			
		||||
		d = ha.NextDialer()
 | 
			
		||||
	}
 | 
			
		||||
	return proxy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *haProxy) Dial(network, addr string) (net.Conn, error) {
 | 
			
		||||
	return p.GetProxy(addr).Dial(network, addr)
 | 
			
		||||
	return d.Dial(network, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								tcptun.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								tcptun.go
									
									
									
									
									
								
							@ -4,15 +4,18 @@ import "net"
 | 
			
		||||
 | 
			
		||||
// TCPTun .
 | 
			
		||||
type TCPTun struct {
 | 
			
		||||
	*proxy
 | 
			
		||||
	*Forwarder
 | 
			
		||||
	sDialer Dialer
 | 
			
		||||
 | 
			
		||||
	raddr string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewTCPTun returns a redirect proxy.
 | 
			
		||||
func NewTCPTun(addr, raddr string, upProxy Proxy) (*TCPTun, error) {
 | 
			
		||||
func NewTCPTun(addr, raddr string, sDialer Dialer) (*TCPTun, error) {
 | 
			
		||||
	s := &TCPTun{
 | 
			
		||||
		proxy: NewProxy(addr, upProxy),
 | 
			
		||||
		raddr: raddr,
 | 
			
		||||
		Forwarder: NewForwarder(addr, nil),
 | 
			
		||||
		sDialer:   sDialer,
 | 
			
		||||
		raddr:     raddr,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s, nil
 | 
			
		||||
@ -42,7 +45,7 @@ func (s *TCPTun) ListenAndServe() {
 | 
			
		||||
				c.SetKeepAlive(true)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			rc, err := s.GetProxy(s.raddr).Dial("tcp", s.raddr)
 | 
			
		||||
			rc, err := s.sDialer.Dial("tcp", s.raddr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
 | 
			
		||||
				logf("failed to connect to target: %v", err)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user