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)
 | 
					- Rule proxy based on destionation: [Config Examples](examples)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO:
 | 
					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
 | 
					- IPSet management
 | 
				
			||||||
- Improve DNS forwarder to resolve domain name and add ip to ipset
 | 
					- Improve DNS forwarder to resolve domain name and add ip to ipset
 | 
				
			||||||
- TUN/TAP device support
 | 
					- 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
 | 
					// Direct proxy
 | 
				
			||||||
var Direct = &direct{}
 | 
					var Direct = &direct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *direct) Addr() string                  { return "127.0.0.1" }
 | 
					func (d *direct) Addr() string { return "DIRECT" }
 | 
				
			||||||
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) Dial(network, addr string) (net.Conn, error) {
 | 
					func (d *direct) Dial(network, addr string) (net.Conn, error) {
 | 
				
			||||||
	c, err := net.Dial(network, addr)
 | 
						c, err := net.Dial(network, addr)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										34
									
								
								dns.go
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								dns.go
									
									
									
									
									
								
							@ -38,7 +38,7 @@ type dnsQuery struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type dnsAnswer struct {
 | 
					type dnsAnswer struct {
 | 
				
			||||||
	DomainName string
 | 
						// DomainName string
 | 
				
			||||||
	QueryType  uint16
 | 
						QueryType  uint16
 | 
				
			||||||
	QueryClass uint16
 | 
						QueryClass uint16
 | 
				
			||||||
	TTL        uint32
 | 
						TTL        uint32
 | 
				
			||||||
@ -48,18 +48,26 @@ type dnsAnswer struct {
 | 
				
			|||||||
	IP string
 | 
						IP string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DNSAnswerHandler .
 | 
				
			||||||
 | 
					type DNSAnswerHandler func(domain, ip string) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DNS .
 | 
					// DNS .
 | 
				
			||||||
type DNS struct {
 | 
					type DNS struct {
 | 
				
			||||||
	*proxy
 | 
						*Forwarder        // as proxy client
 | 
				
			||||||
 | 
						sDialer    Dialer // dialer for server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dnsServer string
 | 
						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
 | 
					// 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{
 | 
						s := &DNS{
 | 
				
			||||||
		proxy:        NewProxy(addr, upProxy),
 | 
							Forwarder: NewForwarder(addr, nil),
 | 
				
			||||||
 | 
							sDialer:   sDialer,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dnsServer:    raddr,
 | 
							dnsServer:    raddr,
 | 
				
			||||||
		dnsServerMap: make(map[string]string),
 | 
							dnsServerMap: make(map[string]string),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -95,8 +103,9 @@ func (s *DNS) ListenAndServe() {
 | 
				
			|||||||
			domain := query.DomainName
 | 
								domain := query.DomainName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			dnsServer := s.GetServer(domain)
 | 
								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 {
 | 
								if err != nil {
 | 
				
			||||||
				logf("failed to connect to server %v: %v", dnsServer, err)
 | 
									logf("failed to connect to server %v: %v", dnsServer, err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
@ -129,6 +138,10 @@ func (s *DNS) ListenAndServe() {
 | 
				
			|||||||
							ip += answer.IP + ","
 | 
												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
 | 
						return s.dnsServer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddAnswerHandler .
 | 
				
			||||||
 | 
					func (s *DNS) AddAnswerHandler(h DNSAnswerHandler) {
 | 
				
			||||||
 | 
						s.answerHandlers = append(s.answerHandlers, h)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseQuery(p []byte) *dnsQuery {
 | 
					func parseQuery(p []byte) *dnsQuery {
 | 
				
			||||||
	q := &dnsQuery{}
 | 
						q := &dnsQuery{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								dnstun.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								dnstun.go
									
									
									
									
									
								
							@ -4,22 +4,26 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// DNSTun .
 | 
					// DNSTun .
 | 
				
			||||||
type DNSTun struct {
 | 
					type DNSTun struct {
 | 
				
			||||||
	*proxy
 | 
						*Forwarder        // as client
 | 
				
			||||||
 | 
						sDialer    Dialer // dialer for server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raddr string
 | 
						raddr string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	udp Proxy
 | 
						udp *DNS
 | 
				
			||||||
	tcp Proxy
 | 
						tcp *TCPTun
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDNSTun returns a dns forwarder.
 | 
					// 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{
 | 
						s := &DNSTun{
 | 
				
			||||||
		proxy: NewProxy(addr, upProxy),
 | 
							Forwarder: NewForwarder(addr, nil),
 | 
				
			||||||
 | 
							sDialer:   sDialer,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		raddr: raddr,
 | 
							raddr: raddr,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.udp, _ = NewDNS(addr, raddr, upProxy)
 | 
						s.udp, _ = NewDNS(addr, raddr, sDialer)
 | 
				
			||||||
	s.tcp, _ = NewTCPTun(addr, raddr, upProxy)
 | 
						s.tcp, _ = NewTCPTun(addr, raddr, sDialer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s, nil
 | 
						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"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTTPProxy .
 | 
					// HTTP .
 | 
				
			||||||
type HTTPProxy struct {
 | 
					type HTTP struct {
 | 
				
			||||||
	*proxy
 | 
						*Forwarder        // as client
 | 
				
			||||||
 | 
						sDialer    Dialer // dialer for server
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewHTTPProxy returns a http proxy.
 | 
					// NewHTTP returns a http proxy.
 | 
				
			||||||
func NewHTTPProxy(addr string, upProxy Proxy) (*HTTPProxy, error) {
 | 
					func NewHTTP(addr string, cDialer Dialer, sDialer Dialer) (*HTTP, error) {
 | 
				
			||||||
	s := &HTTPProxy{
 | 
						s := &HTTP{
 | 
				
			||||||
		proxy: NewProxy(addr, upProxy),
 | 
							Forwarder: NewForwarder(addr, cDialer),
 | 
				
			||||||
 | 
							sDialer:   sDialer,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s, nil
 | 
						return s, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListenAndServe .
 | 
					// ListenAndServe .
 | 
				
			||||||
func (s *HTTPProxy) ListenAndServe() {
 | 
					func (s *HTTP) ListenAndServe() {
 | 
				
			||||||
	l, err := net.Listen("tcp", s.addr)
 | 
						l, err := net.Listen("tcp", s.addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logf("failed to listen on %s: %v", s.addr, err)
 | 
							logf("failed to listen on %s: %v", s.addr, err)
 | 
				
			||||||
@ -53,7 +55,7 @@ func (s *HTTPProxy) ListenAndServe() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve .
 | 
					// Serve .
 | 
				
			||||||
func (s *HTTPProxy) Serve(c net.Conn) {
 | 
					func (s *HTTP) Serve(c net.Conn) {
 | 
				
			||||||
	defer c.Close()
 | 
						defer c.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c, ok := c.(*net.TCPConn); ok {
 | 
						if c, ok := c.(*net.TCPConn); ok {
 | 
				
			||||||
@ -92,7 +94,7 @@ func (s *HTTPProxy) Serve(c net.Conn) {
 | 
				
			|||||||
		tgt += ":80"
 | 
							tgt += ":80"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc, err := s.GetProxy(tgt).Dial("tcp", tgt)
 | 
						rc, err := s.sDialer.Dial("tcp", tgt)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
 | 
							fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
 | 
				
			||||||
		logf("failed to dial: %v", err)
 | 
							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) {
 | 
					func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) {
 | 
				
			||||||
	rc, err := s.GetProxy(requestURI).Dial("tcp", requestURI)
 | 
						rc, err := s.sDialer.Dial("tcp", requestURI)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		c.Write([]byte(proto))
 | 
							c.Write([]byte(proto))
 | 
				
			||||||
		c.Write([]byte(" 502 ERROR\r\n\r\n"))
 | 
							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.
 | 
					// Dial connects to the address addr on the network net via the proxy.
 | 
				
			||||||
func (s *HTTPProxy) Dial(network, addr string) (net.Conn, error) {
 | 
					func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
 | 
				
			||||||
	rc, err := s.GetProxy(s.addr).Dial("tcp", s.addr)
 | 
						rc, err := s.cDialer.Dial("tcp", s.addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logf("dial to %s error: %s", s.addr, err)
 | 
							logf("dial to %s error: %s", s.addr, err)
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										183
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								main.go
									
									
									
									
									
								
							@ -1,155 +1,30 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/signal"
 | 
						"os/signal"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/nadoo/conflag"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VERSION .
 | 
					// VERSION .
 | 
				
			||||||
const VERSION = "0.4.0"
 | 
					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{}) {
 | 
					func logf(f string, v ...interface{}) {
 | 
				
			||||||
	if conf.Verbose {
 | 
						if conf.Verbose {
 | 
				
			||||||
		log.Printf(f, v...)
 | 
							log.Printf(f, v...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func usage() {
 | 
					func dialerFromConf() Dialer {
 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// global forwarders in xx.conf
 | 
						// global forwarders in xx.conf
 | 
				
			||||||
	var forwarders []Proxy
 | 
						var forwarders []Dialer
 | 
				
			||||||
	for _, chain := range conf.Forward {
 | 
						for _, chain := range conf.Forward {
 | 
				
			||||||
		var forward Proxy
 | 
							var forward Dialer
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		for _, url := range strings.Split(chain, ",") {
 | 
							for _, url := range strings.Split(chain, ",") {
 | 
				
			||||||
			forward, err = ProxyFromURL(url, forward)
 | 
								forward, err = DialerFromURL(url, forward)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Fatal(err)
 | 
									log.Fatal(err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -157,36 +32,18 @@ func main() {
 | 
				
			|||||||
		forwarders = append(forwarders, forward)
 | 
							forwarders = append(forwarders, forward)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// combine forwarders to a single strategy forwarder
 | 
						forwarder := NewStrategyDialer(conf.Strategy, forwarders, conf.CheckWebSite, conf.CheckDuration)
 | 
				
			||||||
	forwarder := NewStrategyForwarder(conf.Strategy, forwarders)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// rule forwarders
 | 
						return NewRuleDialer(conf.rules, forwarder)
 | 
				
			||||||
	var ruleForwarders []*RuleForwarder
 | 
					}
 | 
				
			||||||
	for _, ruleFile := range conf.RuleFile {
 | 
					 | 
				
			||||||
		ruleForwarder, err := NewRuleForwarderFromFile(ruleFile)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Fatal(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ruleForwarders = append(ruleForwarders, ruleForwarder)
 | 
					func main() {
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// rules folder
 | 
						confInit()
 | 
				
			||||||
	ruleFolderFiles, _ := listDir(conf.RulesDir, ".rule")
 | 
						sDialer := dialerFromConf()
 | 
				
			||||||
	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)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, listen := range conf.Listen {
 | 
						for _, listen := range conf.Listen {
 | 
				
			||||||
		local, err := ProxyFromURL(listen, forwarder)
 | 
							local, err := ServerFromURL(listen, sDialer)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -194,20 +51,14 @@ func main() {
 | 
				
			|||||||
		go local.ListenAndServe()
 | 
							go local.ListenAndServe()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(forwarders) > 1 {
 | 
					 | 
				
			||||||
		for _, forward := range forwarders {
 | 
					 | 
				
			||||||
			go check(forward, conf.CheckWebSite, conf.CheckDuration)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if conf.DNS != "" {
 | 
						if conf.DNS != "" {
 | 
				
			||||||
		dns, err := NewDNS(conf.DNS, conf.DNSServer[0], forwarder)
 | 
							dns, err := NewDNS(conf.DNS, conf.DNSServer[0], sDialer)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// rule
 | 
							// rule
 | 
				
			||||||
		for _, frwder := range ruleForwarders {
 | 
							for _, frwder := range conf.rules {
 | 
				
			||||||
			for _, domain := range frwder.Domain {
 | 
								for _, domain := range frwder.Domain {
 | 
				
			||||||
				if len(frwder.DNSServer) > 0 {
 | 
									if len(frwder.DNSServer) > 0 {
 | 
				
			||||||
					dns.SetServer(domain, 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()
 | 
							go dns.ListenAndServe()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										108
									
								
								mixed.go
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								mixed.go
									
									
									
									
									
								
							@ -19,23 +19,26 @@ var httpMethods = [...][]byte{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// MixedProxy .
 | 
					// MixedProxy .
 | 
				
			||||||
type MixedProxy struct {
 | 
					type MixedProxy struct {
 | 
				
			||||||
	*proxy
 | 
						sDialer Dialer
 | 
				
			||||||
	http   Proxy
 | 
					
 | 
				
			||||||
	socks5 Proxy
 | 
						addr   string
 | 
				
			||||||
	ss     Proxy
 | 
						http   *HTTP
 | 
				
			||||||
 | 
						socks5 *SOCKS5
 | 
				
			||||||
 | 
						ss     *SS
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewMixedProxy returns a mixed proxy.
 | 
					// 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{
 | 
						p := &MixedProxy{
 | 
				
			||||||
		proxy: NewProxy(addr, upProxy),
 | 
							sDialer: sDialer,
 | 
				
			||||||
 | 
							addr:    addr,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p.http, _ = NewHTTPProxy(addr, upProxy)
 | 
						p.http, _ = NewHTTP(addr, nil, sDialer)
 | 
				
			||||||
	p.socks5, _ = NewSOCKS5Proxy(network, addr, user, pass, upProxy)
 | 
						p.socks5, _ = NewSOCKS5(network, addr, user, pass, nil, sDialer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if user != "" && pass != "" {
 | 
						if user != "" && pass != "" {
 | 
				
			||||||
		p.ss, _ = NewSSProxy(addr, user, pass, upProxy)
 | 
							p.ss, _ = NewSS(addr, user, pass, nil, sDialer)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return p, nil
 | 
						return p, nil
 | 
				
			||||||
@ -58,48 +61,49 @@ func (p *MixedProxy) ListenAndServe() {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		go func() {
 | 
							go p.Serve(c)
 | 
				
			||||||
			defer c.Close()
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
			if c, ok := c.(*net.TCPConn); ok {
 | 
					
 | 
				
			||||||
				c.SetKeepAlive(true)
 | 
					func (p *MixedProxy) Serve(conn net.Conn) {
 | 
				
			||||||
			}
 | 
						defer conn.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			c := newConn(c)
 | 
						if c, ok := conn.(*net.TCPConn); ok {
 | 
				
			||||||
 | 
							c.SetKeepAlive(true)
 | 
				
			||||||
			if p.socks5 != nil {
 | 
						}
 | 
				
			||||||
				head, err := c.Peek(1)
 | 
					
 | 
				
			||||||
				if err != nil {
 | 
						c := newConn(conn)
 | 
				
			||||||
					logf("peek error: %s", err)
 | 
					
 | 
				
			||||||
					return
 | 
						if p.socks5 != nil {
 | 
				
			||||||
				}
 | 
							head, err := c.Peek(1)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
				// check socks5, client send socksversion: 5 as the first byte
 | 
								logf("peek error: %s", err)
 | 
				
			||||||
				if head[0] == socks5Version {
 | 
								return
 | 
				
			||||||
					p.socks5.Serve(c)
 | 
							}
 | 
				
			||||||
					return
 | 
					
 | 
				
			||||||
				}
 | 
							// check socks5, client send socksversion: 5 as the first byte
 | 
				
			||||||
			}
 | 
							if head[0] == socks5Version {
 | 
				
			||||||
 | 
								p.socks5.Serve(c)
 | 
				
			||||||
			if p.http != nil {
 | 
								return
 | 
				
			||||||
				head, err := c.Peek(8)
 | 
							}
 | 
				
			||||||
				if err != nil {
 | 
						}
 | 
				
			||||||
					logf("peek error: %s", err)
 | 
					
 | 
				
			||||||
					return
 | 
						if p.http != nil {
 | 
				
			||||||
				}
 | 
							head, err := c.Peek(8)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
				for _, method := range httpMethods {
 | 
								logf("peek error: %s", err)
 | 
				
			||||||
					if bytes.HasPrefix(head, method) {
 | 
								return
 | 
				
			||||||
						p.http.Serve(c)
 | 
							}
 | 
				
			||||||
						return
 | 
					
 | 
				
			||||||
					}
 | 
							for _, method := range httpMethods {
 | 
				
			||||||
				}
 | 
								if bytes.HasPrefix(head, method) {
 | 
				
			||||||
			}
 | 
									p.http.Serve(c)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
			if p.ss != nil {
 | 
								}
 | 
				
			||||||
				p.ss.Serve(c)
 | 
							}
 | 
				
			||||||
			}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}()
 | 
						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 {
 | 
					type RedirProxy struct {
 | 
				
			||||||
	*proxy
 | 
						*Forwarder        // as client
 | 
				
			||||||
 | 
						sDialer    Dialer // dialer for server
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewRedirProxy returns a redirect proxy.
 | 
					// NewRedirProxy returns a redirect proxy.
 | 
				
			||||||
func NewRedirProxy(addr string, upProxy Proxy) (*RedirProxy, error) {
 | 
					func NewRedirProxy(addr string, sDialer Dialer) (*RedirProxy, error) {
 | 
				
			||||||
	s := &RedirProxy{
 | 
						s := &RedirProxy{
 | 
				
			||||||
		proxy: NewProxy(addr, upProxy),
 | 
							Forwarder: NewForwarder(addr, nil),
 | 
				
			||||||
 | 
							sDialer:   sDialer,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s, nil
 | 
						return s, nil
 | 
				
			||||||
@ -60,7 +62,7 @@ func (s *RedirProxy) ListenAndServe() {
 | 
				
			|||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			rc, err := s.GetProxy(tgt.String()).Dial("tcp", tgt.String())
 | 
								rc, err := s.sDialer.Dial("tcp", tgt.String())
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				logf("failed to connect to target: %v", err)
 | 
									logf("failed to connect to target: %v", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
 | 
				
			|||||||
@ -2,14 +2,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "log"
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RedirProxy .
 | 
					// RedirProxy .
 | 
				
			||||||
type RedirProxy struct{ *proxy }
 | 
					type RedirProxy struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewRedirProxy returns a redirect proxy.
 | 
					// NewRedirProxy returns a redirect proxy.
 | 
				
			||||||
func NewRedirProxy(addr string, upProxy Proxy) (Proxy, error) {
 | 
					func NewRedirProxy(addr string, sDialer Dialer) (*RedirProxy, error) {
 | 
				
			||||||
	return &RedirProxy{proxy: NewProxy(addr, upProxy)}, nil
 | 
						return nil, errors.New("redir not supported on this os")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListenAndServe redirected requests as a server.
 | 
					// ListenAndServe redirected requests as a server.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										150
									
								
								rule.go
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								rule.go
									
									
									
									
									
								
							@ -1,75 +1,119 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
						"net"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/nadoo/conflag"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RuleForwarder , every ruleForwarder points to a rule file
 | 
					// RuleDialer .
 | 
				
			||||||
type RuleForwarder struct {
 | 
					type RuleDialer struct {
 | 
				
			||||||
	Forward       []string
 | 
						gDialer Dialer
 | 
				
			||||||
	Strategy      string
 | 
					 | 
				
			||||||
	CheckWebSite  string
 | 
					 | 
				
			||||||
	CheckDuration int
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DNSServer []string
 | 
						domainMap map[string]Dialer
 | 
				
			||||||
	IPSet     string
 | 
						ipMap     map[string]Dialer
 | 
				
			||||||
 | 
						cidrMap   map[string]Dialer
 | 
				
			||||||
	Domain []string
 | 
					 | 
				
			||||||
	IP     []string
 | 
					 | 
				
			||||||
	CIDR   []string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	name string
 | 
					 | 
				
			||||||
	Proxy
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewRuleForwarderFromFile .
 | 
					// NewRuleDialer .
 | 
				
			||||||
func NewRuleForwarderFromFile(ruleFile string) (*RuleForwarder, error) {
 | 
					func NewRuleDialer(rules []*RuleConf, gDialer Dialer) Dialer {
 | 
				
			||||||
	p := &RuleForwarder{name: ruleFile}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	f := conflag.NewFromFile("rule", ruleFile)
 | 
						if len(rules) == 0 {
 | 
				
			||||||
	f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT[,SCHEMA://[USER|METHOD:PASSWORD@][HOST]:PORT]")
 | 
							return gDialer
 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var forwarders []Proxy
 | 
						rd := &RuleDialer{gDialer: gDialer}
 | 
				
			||||||
	for _, chain := range p.Forward {
 | 
					
 | 
				
			||||||
		var forward Proxy
 | 
						for _, r := range rules {
 | 
				
			||||||
		var err error
 | 
							var forwarders []Dialer
 | 
				
			||||||
		for _, url := range strings.Split(chain, ",") {
 | 
							for _, chain := range r.Forward {
 | 
				
			||||||
			forward, err = ProxyFromURL(url, 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 {
 | 
								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 {
 | 
							// find in domainMap
 | 
				
			||||||
		go check(forward, p.CheckWebSite, p.CheckDuration)
 | 
							if d, ok := p.domainMap[domain]; ok {
 | 
				
			||||||
 | 
								return d
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p.Proxy = forwarder
 | 
						return p.gDialer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
	return p, err
 | 
					
 | 
				
			||||||
 | 
					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",
 | 
						"address type not supported",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SOCKS5Proxy .
 | 
					// SOCKS5 .
 | 
				
			||||||
type SOCKS5Proxy struct {
 | 
					type SOCKS5 struct {
 | 
				
			||||||
	*proxy
 | 
						*Forwarder
 | 
				
			||||||
 | 
						sDialer Dialer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	network  string
 | 
						network  string
 | 
				
			||||||
	user     string
 | 
						user     string
 | 
				
			||||||
	password 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.
 | 
					// with an optional username and password. See RFC 1928.
 | 
				
			||||||
func NewSOCKS5Proxy(network, addr, user, pass string, upProxy Proxy) (*SOCKS5Proxy, error) {
 | 
					func NewSOCKS5(network, addr, user, pass string, cDialer Dialer, sDialer Dialer) (*SOCKS5, error) {
 | 
				
			||||||
	s := &SOCKS5Proxy{
 | 
						s := &SOCKS5{
 | 
				
			||||||
		proxy:    NewProxy(addr, upProxy),
 | 
							Forwarder: NewForwarder(addr, cDialer),
 | 
				
			||||||
		user:     user,
 | 
							sDialer:   sDialer,
 | 
				
			||||||
		password: pass,
 | 
							user:      user,
 | 
				
			||||||
 | 
							password:  pass,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s, nil
 | 
						return s, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListenAndServe connects to the address addr on the network net via the SOCKS5 proxy.
 | 
					// 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)
 | 
						l, err := net.Listen("tcp", s.addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logf("failed to listen on %s: %v", s.addr, err)
 | 
							logf("failed to listen on %s: %v", s.addr, err)
 | 
				
			||||||
@ -97,7 +100,7 @@ func (s *SOCKS5Proxy) ListenAndServe() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve .
 | 
					// Serve .
 | 
				
			||||||
func (s *SOCKS5Proxy) Serve(c net.Conn) {
 | 
					func (s *SOCKS5) Serve(c net.Conn) {
 | 
				
			||||||
	defer c.Close()
 | 
						defer c.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c, ok := c.(*net.TCPConn); ok {
 | 
						if c, ok := c.(*net.TCPConn); ok {
 | 
				
			||||||
@ -110,7 +113,7 @@ func (s *SOCKS5Proxy) Serve(c net.Conn) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc, err := s.GetProxy(tgt.String()).Dial("tcp", tgt.String())
 | 
						rc, err := s.sDialer.Dial("tcp", tgt.String())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logf("failed to connect to target: %v", err)
 | 
							logf("failed to connect to target: %v", err)
 | 
				
			||||||
		return
 | 
							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.
 | 
					// 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 {
 | 
						switch network {
 | 
				
			||||||
	case "tcp", "tcp6", "tcp4":
 | 
						case "tcp", "tcp6", "tcp4":
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		logf("dial to %s error: %s", s.addr, err)
 | 
							logf("dial to %s error: %s", s.addr, err)
 | 
				
			||||||
		return nil, 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,
 | 
					// 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 *SOCKS5Proxy) 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 {
 | 
				
			||||||
		return err
 | 
							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.
 | 
					// 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.
 | 
						// Read RFC 1928 for request and reply structure and sizes.
 | 
				
			||||||
	buf := make([]byte, MaxAddrLen)
 | 
						buf := make([]byte, MaxAddrLen)
 | 
				
			||||||
	// read VER, NMETHODS, METHODS
 | 
						// read VER, NMETHODS, METHODS
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								ss.go
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								ss.go
									
									
									
									
									
								
							@ -9,21 +9,24 @@ import (
 | 
				
			|||||||
	"github.com/shadowsocks/go-shadowsocks2/core"
 | 
						"github.com/shadowsocks/go-shadowsocks2/core"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SSProxy .
 | 
					// SS .
 | 
				
			||||||
type SSProxy struct {
 | 
					type SS struct {
 | 
				
			||||||
	*proxy
 | 
						*Forwarder
 | 
				
			||||||
 | 
						sDialer Dialer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	core.StreamConnCipher
 | 
						core.StreamConnCipher
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewSSProxy returns a shadowsocks proxy.
 | 
					// NewSS returns a shadowsocks proxy.
 | 
				
			||||||
func NewSSProxy(addr, method, pass string, upProxy Proxy) (*SSProxy, error) {
 | 
					func NewSS(addr, method, pass string, cDialer Dialer, sDialer Dialer) (*SS, error) {
 | 
				
			||||||
	ciph, err := core.PickCipher(method, nil, pass)
 | 
						ciph, err := core.PickCipher(method, nil, pass)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("PickCipher for '%s', error: %s", method, err)
 | 
							log.Fatalf("PickCipher for '%s', error: %s", method, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s := &SSProxy{
 | 
						s := &SS{
 | 
				
			||||||
		proxy:            NewProxy(addr, upProxy),
 | 
							Forwarder:        NewForwarder(addr, cDialer),
 | 
				
			||||||
 | 
							sDialer:          sDialer,
 | 
				
			||||||
		StreamConnCipher: ciph,
 | 
							StreamConnCipher: ciph,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,7 +34,7 @@ func NewSSProxy(addr, method, pass string, upProxy Proxy) (*SSProxy, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListenAndServe shadowsocks requests as a server.
 | 
					// ListenAndServe shadowsocks requests as a server.
 | 
				
			||||||
func (s *SSProxy) ListenAndServe() {
 | 
					func (s *SS) ListenAndServe() {
 | 
				
			||||||
	l, err := net.Listen("tcp", s.addr)
 | 
						l, err := net.Listen("tcp", s.addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logf("failed to listen on %s: %v", s.addr, err)
 | 
							logf("failed to listen on %s: %v", s.addr, err)
 | 
				
			||||||
@ -51,7 +54,7 @@ func (s *SSProxy) ListenAndServe() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve .
 | 
					// Serve .
 | 
				
			||||||
func (s *SSProxy) Serve(c net.Conn) {
 | 
					func (s *SS) Serve(c net.Conn) {
 | 
				
			||||||
	defer c.Close()
 | 
						defer c.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c, ok := c.(*net.TCPConn); ok {
 | 
						if c, ok := c.(*net.TCPConn); ok {
 | 
				
			||||||
@ -66,7 +69,7 @@ func (s *SSProxy) Serve(c net.Conn) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc, err := s.GetProxy(tgt.String()).Dial("tcp", tgt.String())
 | 
						rc, err := s.sDialer.Dial("tcp", tgt.String())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		logf("failed to connect to target: %v", err)
 | 
							logf("failed to connect to target: %v", err)
 | 
				
			||||||
		return
 | 
							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.
 | 
					// 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)
 | 
						target := ParseAddr(addr)
 | 
				
			||||||
	if target == nil {
 | 
						if target == nil {
 | 
				
			||||||
		return nil, errors.New("Unable to parse address: " + addr)
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		logf("dial to %s error: %s", s.addr, err)
 | 
							logf("dial to %s error: %s", s.addr, err)
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										159
									
								
								strategy.go
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								strategy.go
									
									
									
									
									
								
							@ -1,64 +1,80 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "net"
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewStrategyForwarder .
 | 
					// NewStrategyDialer .
 | 
				
			||||||
func NewStrategyForwarder(strategy string, forwarders []Proxy) Proxy {
 | 
					func NewStrategyDialer(strategy string, dialers []Dialer, website string, duration int) Dialer {
 | 
				
			||||||
	var proxy Proxy
 | 
						var dialer Dialer
 | 
				
			||||||
	if len(forwarders) == 0 {
 | 
						if len(dialers) == 0 {
 | 
				
			||||||
		proxy = Direct
 | 
							dialer = Direct
 | 
				
			||||||
	} else if len(forwarders) == 1 {
 | 
						} else if len(dialers) == 1 {
 | 
				
			||||||
		proxy = forwarders[0]
 | 
							dialer = dialers[0]
 | 
				
			||||||
	} else if len(forwarders) > 1 {
 | 
						} else if len(dialers) > 1 {
 | 
				
			||||||
		switch strategy {
 | 
							switch strategy {
 | 
				
			||||||
		case "rr":
 | 
							case "rr":
 | 
				
			||||||
			proxy = newRRProxy("", forwarders)
 | 
								dialer = newRRDialer(dialers, website, duration)
 | 
				
			||||||
			logf("forward to remote servers in round robin mode.")
 | 
								logf("forward to remote servers in round robin mode.")
 | 
				
			||||||
		case "ha":
 | 
							case "ha":
 | 
				
			||||||
			proxy = newHAProxy("", forwarders)
 | 
								dialer = newHADialer(dialers, website, duration)
 | 
				
			||||||
			logf("forward to remote servers in high availability mode.")
 | 
								logf("forward to remote servers in high availability mode.")
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			logf("not supported forward mode '%s', just use the first forward server.", conf.Strategy)
 | 
								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
 | 
					// rrDialer
 | 
				
			||||||
type rrProxy struct {
 | 
					type rrDialer struct {
 | 
				
			||||||
	forwarders []Proxy
 | 
						dialers []Dialer
 | 
				
			||||||
	idx        int
 | 
						idx     int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						status map[int]bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// for checking
 | 
				
			||||||
 | 
						website  string
 | 
				
			||||||
 | 
						duration int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// newRRProxy .
 | 
					// newRRDialer .
 | 
				
			||||||
func newRRProxy(addr string, forwarders []Proxy) Proxy {
 | 
					func newRRDialer(dialers []Dialer, website string, duration int) *rrDialer {
 | 
				
			||||||
	if len(forwarders) == 0 {
 | 
						rr := &rrDialer{dialers: dialers}
 | 
				
			||||||
		return Direct
 | 
					
 | 
				
			||||||
	} else if len(forwarders) == 1 {
 | 
						rr.status = make(map[int]bool)
 | 
				
			||||||
		return NewProxy(addr, forwarders[0])
 | 
						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 (rr *rrDialer) Addr() string { return "STRATEGY" }
 | 
				
			||||||
func (p *rrProxy) ListenAndServe()               {}
 | 
					func (rr *rrDialer) Dial(network, addr string) (net.Conn, error) {
 | 
				
			||||||
func (p *rrProxy) Serve(c net.Conn)              {}
 | 
						return rr.NextDialer().Dial(network, addr)
 | 
				
			||||||
func (p *rrProxy) CurrentProxy() Proxy           { return p.forwarders[p.idx] }
 | 
					}
 | 
				
			||||||
func (p *rrProxy) GetProxy(dstAddr string) Proxy { return p.NextProxy() }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *rrProxy) NextProxy() Proxy {
 | 
					func (rr *rrDialer) NextDialer() Dialer {
 | 
				
			||||||
	n := len(p.forwarders)
 | 
						n := len(rr.dialers)
 | 
				
			||||||
	if n == 1 {
 | 
						if n == 1 {
 | 
				
			||||||
		return p.forwarders[0]
 | 
							rr.idx = 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	found := false
 | 
						found := false
 | 
				
			||||||
	for i := 0; i < n; i++ {
 | 
						for i := 0; i < n; i++ {
 | 
				
			||||||
		p.idx = (p.idx + 1) % n
 | 
							rr.idx = (rr.idx + 1) % n
 | 
				
			||||||
		if p.forwarders[p.idx].Enabled() {
 | 
							if rr.status[rr.idx] {
 | 
				
			||||||
			found = true
 | 
								found = true
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -68,34 +84,73 @@ func (p *rrProxy) NextProxy() Proxy {
 | 
				
			|||||||
		logf("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.")
 | 
							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 }
 | 
					// Check dialer
 | 
				
			||||||
func (p *rrProxy) SetEnable(enable bool) {}
 | 
					func (rr *rrDialer) checkDialer(idx int) {
 | 
				
			||||||
 | 
						retry := 1
 | 
				
			||||||
 | 
						buf := make([]byte, 4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *rrProxy) Dial(network, addr string) (net.Conn, error) {
 | 
						if strings.IndexByte(rr.website, ':') == -1 {
 | 
				
			||||||
	return p.GetProxy(addr).Dial(network, addr)
 | 
							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
 | 
					// high availability proxy
 | 
				
			||||||
type haProxy struct {
 | 
					type haDialer struct {
 | 
				
			||||||
	Proxy
 | 
						*rrDialer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// newHAProxy .
 | 
					// newHADialer .
 | 
				
			||||||
func newHAProxy(addr string, forwarders []Proxy) Proxy {
 | 
					func newHADialer(dialers []Dialer, webhost string, duration int) Dialer {
 | 
				
			||||||
	return &haProxy{Proxy: newRRProxy(addr, forwarders)}
 | 
						return &haDialer{rrDialer: newRRDialer(dialers, webhost, duration)}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *haProxy) GetProxy(dstAddr string) Proxy {
 | 
					func (ha *haDialer) Dial(network, addr string) (net.Conn, error) {
 | 
				
			||||||
	proxy := p.CurrentProxy()
 | 
						d := ha.dialers[ha.idx]
 | 
				
			||||||
	if proxy.Enabled() == false {
 | 
					
 | 
				
			||||||
		return p.NextProxy()
 | 
						if !ha.status[ha.idx] {
 | 
				
			||||||
 | 
							d = ha.NextDialer()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return proxy
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *haProxy) Dial(network, addr string) (net.Conn, error) {
 | 
						return d.Dial(network, addr)
 | 
				
			||||||
	return p.GetProxy(addr).Dial(network, addr)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								tcptun.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								tcptun.go
									
									
									
									
									
								
							@ -4,15 +4,18 @@ import "net"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TCPTun .
 | 
					// TCPTun .
 | 
				
			||||||
type TCPTun struct {
 | 
					type TCPTun struct {
 | 
				
			||||||
	*proxy
 | 
						*Forwarder
 | 
				
			||||||
 | 
						sDialer Dialer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raddr string
 | 
						raddr string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTCPTun returns a redirect proxy.
 | 
					// 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{
 | 
						s := &TCPTun{
 | 
				
			||||||
		proxy: NewProxy(addr, upProxy),
 | 
							Forwarder: NewForwarder(addr, nil),
 | 
				
			||||||
		raddr: raddr,
 | 
							sDialer:   sDialer,
 | 
				
			||||||
 | 
							raddr:     raddr,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s, nil
 | 
						return s, nil
 | 
				
			||||||
@ -42,7 +45,7 @@ func (s *TCPTun) ListenAndServe() {
 | 
				
			|||||||
				c.SetKeepAlive(true)
 | 
									c.SetKeepAlive(true)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			rc, err := s.GetProxy(s.raddr).Dial("tcp", s.raddr)
 | 
								rc, err := s.sDialer.Dial("tcp", s.raddr)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				logf("failed to connect to target: %v", err)
 | 
									logf("failed to connect to target: %v", err)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user