github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/proxy/per_host.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package proxy 6 7 import ( 8 "context" 9 "net" 10 "strings" 11 ) 12 13 // A PerHost directs connections to a default Dialer unless the host name 14 // requested matches one of a number of exceptions. 15 type PerHost struct { 16 def, bypass Dialer 17 18 bypassNetworks []*net.IPNet 19 bypassIPs []net.IP 20 bypassZones []string 21 bypassHosts []string 22 } 23 24 // NewPerHost returns a PerHost Dialer that directs connections to either 25 // defaultDialer or bypass, depending on whether the connection matches one of 26 // the configured rules. 27 func NewPerHost(defaultDialer, bypass Dialer) *PerHost { 28 return &PerHost{ 29 def: defaultDialer, 30 bypass: bypass, 31 } 32 } 33 34 // Dial connects to the address addr on the given network through either 35 // defaultDialer or bypass. 36 func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) { 37 host, _, err := net.SplitHostPort(addr) 38 if err != nil { 39 return nil, err 40 } 41 42 return p.dialerForRequest(host).Dial(network, addr) 43 } 44 45 // DialContext connects to the address addr on the given network through either 46 // defaultDialer or bypass. 47 func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) { 48 host, _, err := net.SplitHostPort(addr) 49 if err != nil { 50 return nil, err 51 } 52 d := p.dialerForRequest(host) 53 if x, ok := d.(ContextDialer); ok { 54 return x.DialContext(ctx, network, addr) 55 } 56 return dialContext(ctx, d, network, addr) 57 } 58 59 func (p *PerHost) dialerForRequest(host string) Dialer { 60 if ip := net.ParseIP(host); ip != nil { 61 for _, net := range p.bypassNetworks { 62 if net.Contains(ip) { 63 return p.bypass 64 } 65 } 66 for _, bypassIP := range p.bypassIPs { 67 if bypassIP.Equal(ip) { 68 return p.bypass 69 } 70 } 71 return p.def 72 } 73 74 for _, zone := range p.bypassZones { 75 if strings.HasSuffix(host, zone) { 76 return p.bypass 77 } 78 if host == zone[1:] { 79 // For a zone ".example.com", we match "example.com" 80 // too. 81 return p.bypass 82 } 83 } 84 for _, bypassHost := range p.bypassHosts { 85 if bypassHost == host { 86 return p.bypass 87 } 88 } 89 return p.def 90 } 91 92 // AddFromString parses a string that contains comma-separated values 93 // specifying hosts that should use the bypass proxy. Each value is either an 94 // IP address, a CIDR range, a zone (*.example.com) or a host name 95 // (localhost). A best effort is made to parse the string and errors are 96 // ignored. 97 func (p *PerHost) AddFromString(s string) { 98 hosts := strings.Split(s, ",") 99 for _, host := range hosts { 100 host = strings.TrimSpace(host) 101 if len(host) == 0 { 102 continue 103 } 104 if strings.Contains(host, "/") { 105 // We assume that it's a CIDR address like 127.0.0.0/8 106 if _, net, err := net.ParseCIDR(host); err == nil { 107 p.AddNetwork(net) 108 } 109 continue 110 } 111 if ip := net.ParseIP(host); ip != nil { 112 p.AddIP(ip) 113 continue 114 } 115 if strings.HasPrefix(host, "*.") { 116 p.AddZone(host[1:]) 117 continue 118 } 119 p.AddHost(host) 120 } 121 } 122 123 // AddIP specifies an IP address that will use the bypass proxy. Note that 124 // this will only take effect if a literal IP address is dialed. A connection 125 // to a named host will never match an IP. 126 func (p *PerHost) AddIP(ip net.IP) { 127 p.bypassIPs = append(p.bypassIPs, ip) 128 } 129 130 // AddNetwork specifies an IP range that will use the bypass proxy. Note that 131 // this will only take effect if a literal IP address is dialed. A connection 132 // to a named host will never match. 133 func (p *PerHost) AddNetwork(net *net.IPNet) { 134 p.bypassNetworks = append(p.bypassNetworks, net) 135 } 136 137 // AddZone specifies a DNS suffix that will use the bypass proxy. A zone of 138 // "example.com" matches "example.com" and all of its subdomains. 139 func (p *PerHost) AddZone(zone string) { 140 if strings.HasSuffix(zone, ".") { 141 zone = zone[:len(zone)-1] 142 } 143 if !strings.HasPrefix(zone, ".") { 144 zone = "." + zone 145 } 146 p.bypassZones = append(p.bypassZones, zone) 147 } 148 149 // AddHost specifies a host name that will use the bypass proxy. 150 func (p *PerHost) AddHost(host string) { 151 if strings.HasSuffix(host, ".") { 152 host = host[:len(host)-1] 153 } 154 p.bypassHosts = append(p.bypassHosts, host) 155 }