// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package proxy

import (
	
	
	
)

// A PerHost directs connections to a default Dialer unless the host name
// requested matches one of a number of exceptions.
type PerHost struct {
	def, bypass Dialer

	bypassNetworks []*net.IPNet
	bypassIPs      []net.IP
	bypassZones    []string
	bypassHosts    []string
}

// NewPerHost returns a PerHost Dialer that directs connections to either
// defaultDialer or bypass, depending on whether the connection matches one of
// the configured rules.
func (,  Dialer) *PerHost {
	return &PerHost{
		def:    ,
		bypass: ,
	}
}

// Dial connects to the address addr on the given network through either
// defaultDialer or bypass.
func ( *PerHost) (,  string) ( net.Conn,  error) {
	, ,  := net.SplitHostPort()
	if  != nil {
		return nil, 
	}

	return .dialerForRequest().Dial(, )
}

// DialContext connects to the address addr on the given network through either
// defaultDialer or bypass.
func ( *PerHost) ( context.Context, ,  string) ( net.Conn,  error) {
	, ,  := net.SplitHostPort()
	if  != nil {
		return nil, 
	}
	 := .dialerForRequest()
	if ,  := .(ContextDialer);  {
		return .DialContext(, , )
	}
	return dialContext(, , , )
}

func ( *PerHost) ( string) Dialer {
	if  := net.ParseIP();  != nil {
		for ,  := range .bypassNetworks {
			if .Contains() {
				return .bypass
			}
		}
		for ,  := range .bypassIPs {
			if .Equal() {
				return .bypass
			}
		}
		return .def
	}

	for ,  := range .bypassZones {
		if strings.HasSuffix(, ) {
			return .bypass
		}
		if  == [1:] {
			// For a zone ".example.com", we match "example.com"
			// too.
			return .bypass
		}
	}
	for ,  := range .bypassHosts {
		if  ==  {
			return .bypass
		}
	}
	return .def
}

// AddFromString parses a string that contains comma-separated values
// specifying hosts that should use the bypass proxy. Each value is either an
// IP address, a CIDR range, a zone (*.example.com) or a host name
// (localhost). A best effort is made to parse the string and errors are
// ignored.
func ( *PerHost) ( string) {
	 := strings.Split(, ",")
	for ,  := range  {
		 = strings.TrimSpace()
		if len() == 0 {
			continue
		}
		if strings.Contains(, "/") {
			// We assume that it's a CIDR address like 127.0.0.0/8
			if , ,  := net.ParseCIDR();  == nil {
				.AddNetwork()
			}
			continue
		}
		if  := net.ParseIP();  != nil {
			.AddIP()
			continue
		}
		if strings.HasPrefix(, "*.") {
			.AddZone([1:])
			continue
		}
		.AddHost()
	}
}

// AddIP specifies an IP address that will use the bypass proxy. Note that
// this will only take effect if a literal IP address is dialed. A connection
// to a named host will never match an IP.
func ( *PerHost) ( net.IP) {
	.bypassIPs = append(.bypassIPs, )
}

// AddNetwork specifies an IP range that will use the bypass proxy. Note that
// this will only take effect if a literal IP address is dialed. A connection
// to a named host will never match.
func ( *PerHost) ( *net.IPNet) {
	.bypassNetworks = append(.bypassNetworks, )
}

// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
// "example.com" matches "example.com" and all of its subdomains.
func ( *PerHost) ( string) {
	if strings.HasSuffix(, ".") {
		 = [:len()-1]
	}
	if !strings.HasPrefix(, ".") {
		 = "." + 
	}
	.bypassZones = append(.bypassZones, )
}

// AddHost specifies a host name that will use the bypass proxy.
func ( *PerHost) ( string) {
	if strings.HasSuffix(, ".") {
		 = [:len()-1]
	}
	.bypassHosts = append(.bypassHosts, )
}