package proxyproto

import (
	
	
	
)

// PolicyFunc can be used to decide whether to trust the PROXY info from
// upstream. If set, the connecting address is passed in as an argument.
//
// See below for the different policies.
//
// In case an error is returned the connection is denied.
//
// Deprecated: use ConnPolicyFunc instead.
type PolicyFunc func(upstream net.Addr) (Policy, error)

// ConnPolicyFunc can be used to decide whether to trust the PROXY info
// based on connection policy options. If set, the connecting addresses
// (remote and local) are passed in as argument.
//
// See below for the different policies.
//
// In case an error is returned the connection is denied.
type ConnPolicyFunc func(connPolicyOptions ConnPolicyOptions) (Policy, error)

// ConnPolicyOptions contains the remote and local addresses of a connection.
type ConnPolicyOptions struct {
	Upstream   net.Addr
	Downstream net.Addr
}

// Policy defines how a connection with a PROXY header address is treated.
type Policy int

const (
	// USE address from PROXY header.
	USE Policy = iota
	// IGNORE address from PROXY header, but accept connection.
	IGNORE
	// REJECT connection when PROXY header is sent
	// Note: even though the first read on the connection returns an error if
	// a PROXY header is present, subsequent reads do not. It is the task of
	// the code using the connection to handle that case properly.
	REJECT
	// REQUIRE connection to send PROXY header, reject if not present
	// Note: even though the first read on the connection returns an error if
	// a PROXY header is not present, subsequent reads do not. It is the task
	// of the code using the connection to handle that case properly.
	REQUIRE
	// SKIP accepts a connection without requiring the PROXY header.
	// Note: an example usage can be found in the SkipProxyHeaderForCIDR
	// function.
	SKIP
)

// ConnSkipProxyHeaderForCIDR returns a ConnPolicyFunc which can be used to accept
// a connection from a skipHeaderCIDR without requiring a PROXY header, e.g.
// Kubernetes pods local traffic. The def is a policy to use when an upstream
// address doesn't match the skipHeaderCIDR.
func ( *net.IPNet,  Policy) ConnPolicyFunc {
	return func( ConnPolicyOptions) (Policy, error) {
		,  := ipFromAddr(.Upstream)
		if  != nil {
			return , 
		}

		if  != nil && .Contains() {
			return SKIP, nil
		}

		return , nil
	}
}

// SkipProxyHeaderForCIDR returns a PolicyFunc which can be used to accept a
// connection from a skipHeaderCIDR without requiring a PROXY header, e.g.
// Kubernetes pods local traffic. The def is a policy to use when an upstream
// address doesn't match the skipHeaderCIDR.
//
// Deprecated: use ConnSkipProxyHeaderForCIDR instead.
func ( *net.IPNet,  Policy) PolicyFunc {
	 := ConnSkipProxyHeaderForCIDR(, )
	return func( net.Addr) (Policy, error) {
		return (ConnPolicyOptions{Upstream: })
	}
}

// WithPolicy adds given policy to a connection when passed as option to NewConn().
func ( Policy) func(*Conn) {
	return func( *Conn) {
		.ProxyHeaderPolicy = 
	}
}

// ConnLaxWhiteListPolicy returns a ConnPolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list the proxy
// header will be ignored. If one of the provided IP addresses or IP ranges
// is invalid it will return an error instead of a ConnPolicyFunc.
func ( []string) (ConnPolicyFunc, error) {
	,  := parse()
	if  != nil {
		return nil, 
	}

	return connWhitelistPolicy(, IGNORE), nil
}

// LaxWhiteListPolicy returns a PolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list the proxy
// header will be ignored. If one of the provided IP addresses or IP ranges
// is invalid it will return an error instead of a PolicyFunc.
//
// Deprecated: use ConnLaxWhiteListPolicy instead.
func ( []string) (PolicyFunc, error) {
	,  := ConnLaxWhiteListPolicy()
	if  != nil {
		return nil, 
	}

	return func( net.Addr) (Policy, error) {
		return (ConnPolicyOptions{Upstream: })
	}, nil
}

// ConnMustLaxWhiteListPolicy returns a ConnLaxWhiteListPolicy but will panic
// if one of the provided IP addresses or IP ranges is invalid.
func ( []string) ConnPolicyFunc {
	,  := ConnLaxWhiteListPolicy()
	if  != nil {
		panic()
	}

	return 
}

// MustLaxWhiteListPolicy returns a LaxWhiteListPolicy but will panic if one
// of the provided IP addresses or IP ranges is invalid.
//
// Deprecated: use ConnMustLaxWhiteListPolicy instead.
func ( []string) PolicyFunc {
	 := ConnMustLaxWhiteListPolicy()
	return func( net.Addr) (Policy, error) {
		return (ConnPolicyOptions{Upstream: })
	}
}

// ConnStrictWhiteListPolicy returns a ConnPolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list reading on
// the connection will be refused on the first read. Please note: subsequent
// reads do not error. It is the task of the code using the connection to
// handle that case properly. If one of the provided IP addresses or IP
// ranges is invalid it will return an error instead of a ConnPolicyFunc.
func ( []string) (ConnPolicyFunc, error) {
	,  := parse()
	if  != nil {
		return nil, 
	}

	return connWhitelistPolicy(, REJECT), nil
}

// StrictWhiteListPolicy returns a PolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list reading on
// the connection will be refused on the first read. Please note: subsequent
// reads do not error. It is the task of the code using the connection to
// handle that case properly. If one of the provided IP addresses or IP
// ranges is invalid it will return an error instead of a PolicyFunc.
//
// Deprecated: use ConnStrictWhiteListPolicy instead.
func ( []string) (PolicyFunc, error) {
	,  := ConnStrictWhiteListPolicy()
	if  != nil {
		return nil, 
	}

	return func( net.Addr) (Policy, error) {
		return (ConnPolicyOptions{Upstream: })
	}, nil
}

// ConnMustStrictWhiteListPolicy returns a ConnStrictWhiteListPolicy but will panic
// if one of the provided IP addresses or IP ranges is invalid.
func ( []string) ConnPolicyFunc {
	,  := ConnStrictWhiteListPolicy()
	if  != nil {
		panic()
	}

	return 
}

// MustStrictWhiteListPolicy returns a StrictWhiteListPolicy but will panic
// if one of the provided IP addresses or IP ranges is invalid.
//
// Deprecated: use ConnMustStrictWhiteListPolicy instead.
func ( []string) PolicyFunc {
	 := ConnMustStrictWhiteListPolicy()
	return func( net.Addr) (Policy, error) {
		return (ConnPolicyOptions{Upstream: })
	}
}

func connWhitelistPolicy( []func(net.IP) bool,  Policy) ConnPolicyFunc {
	return func( ConnPolicyOptions) (Policy, error) {
		,  := ipFromAddr(.Upstream)
		if  != nil {
			// something is wrong with the source IP, better reject the connection
			return REJECT, 
		}

		for ,  := range  {
			if () {
				return USE, nil
			}
		}

		return , nil
	}
}

func parse( []string) ([]func(net.IP) bool, error) {
	 := make([]func(net.IP) bool, len())
	for ,  := range  {
		if strings.LastIndex(, "/") > 0 {
			, ,  := net.ParseCIDR()
			if  != nil {
				return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP range: %v", , )
			}

			[] = .Contains
		} else {
			 := net.ParseIP()
			if  == nil {
				return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP address", )
			}

			[] = .Equal
		}
	}

	return , nil
}

func ipFromAddr( net.Addr) (net.IP, error) {
	, ,  := net.SplitHostPort(.String())
	if  != nil {
		return nil, 
	}

	 := net.ParseIP()
	if nil ==  {
		return nil, fmt.Errorf("proxyproto: invalid IP address")
	}

	return , nil
}

// TrustProxyHeaderFrom returns a ConnPolicyFunc which can be used to decide
// whether to use or reject PROXY headers based on the source IP of the
// connection. This policy ensures that only trusted sources can set the PROXY
// header. Connections from IPs not in the trusted list will be rejected.
func ( ...net.IP) ConnPolicyFunc {
	return func( ConnPolicyOptions) (Policy, error) {
		,  := ipFromAddr(.Upstream)
		if  != nil {
			return REJECT, 
		}

		for ,  := range  {
			if .Equal() {
				return USE, nil
			}
		}

		return REJECT, nil
	}
}

// IgnoreProxyHeaderNotOnInterface returns a ConnPolicyFunc which can be used to
// decide whether to use or ignore PROXY headers depending on the connection
// being made on specific interfaces. This policy can be used when the server
// is bound to multiple interfaces but wants to allow on one or more interfaces.
func ( net.IP) ConnPolicyFunc {
	return func( ConnPolicyOptions) (Policy, error) {
		,  := ipFromAddr(.Downstream)
		if  != nil {
			return REJECT, 
		}

		if .Equal() {
			return USE, nil
		}

		return IGNORE, nil
	}
}