package reuseport

import (
	
	
	
	

	
)

type dialer struct {
	// All address that are _not_ loopback or unspecified (0.0.0.0 or ::).
	specific []*net.TCPAddr
	// All loopback addresses (127.*.*.*, ::1).
	loopback []*net.TCPAddr
	// Unspecified addresses (0.0.0.0, ::)
	unspecified []*net.TCPAddr
}

func ( *dialer) (,  string) (net.Conn, error) {
	return .DialContext(context.Background(), , )
}

func randAddr( []*net.TCPAddr) *net.TCPAddr {
	if len() > 0 {
		return [rand.Intn(len())]
	}
	return nil
}

// DialContext dials a target addr.
//
// In-order:
//
//  1. If we're _explicitly_ listening on the preferred source address for the destination address
//     (per the system's routes), we'll use that listener's port as the source port.
//  2. If we're listening on one or more _unspecified_ addresses (zero address), we'll pick a source
//     port from one of these listener's.
//  3. Otherwise, we'll let the system pick the source port.
func ( *dialer) ( context.Context, ,  string) (net.Conn, error) {
	// We only check this case if the user is listening on a specific address (loopback or
	// otherwise). Generally, users will listen on the "unspecified" address (0.0.0.0 or ::) and
	// we can skip this section.
	//
	// This lets us avoid resolving the address twice, in most cases.
	if len(.specific) > 0 || len(.loopback) > 0 {
		,  := net.ResolveTCPAddr(, )
		if  != nil {
			return nil, 
		}
		 := .IP
		if !.IsLoopback() && !.IsGlobalUnicast() {
			return nil, fmt.Errorf("undialable IP: %s", )
		}

		// If we're listening on some specific address and that specific address happens to
		// be the preferred source address for the target destination address, we try to
		// dial with that address/port.
		//
		// We skip this check if we _aren't_ listening on any specific addresses, because
		// checking routing tables can be expensive and users rarely listen on specific IP
		// addresses.
		if len(.specific) > 0 {
			if ,  := netroute.New();  == nil {
				if , , ,  := .Route();  == nil {
					for ,  := range .specific {
						if .IP.Equal() {
							return reuseDial(, , , )
						}
					}
				}
			}
		}

		// Otherwise, if we are listening on a loopback address and the destination is also
		// a loopback address, use the port from our loopback listener.
		if len(.loopback) > 0 && .IsLoopback() {
			return reuseDial(, randAddr(.loopback), , )
		}
	}

	// If we're listening on any uspecified addresses, use a randomly chosen port from one of
	// these listeners.
	if len(.unspecified) > 0 {
		return reuseDial(, randAddr(.unspecified), , )
	}

	// Finally, just pick a random port.
	var  net.Dialer
	return .DialContext(, , )
}

func newDialer( map[*listener]struct{}) *dialer {
	 := make([]*net.TCPAddr, 0)
	 := make([]*net.TCPAddr, 0)
	 := make([]*net.TCPAddr, 0)

	for  := range  {
		 := .Addr().(*net.TCPAddr)
		if .IP.IsLoopback() {
			 = append(, )
		} else if .IP.IsUnspecified() {
			 = append(, )
		} else {
			 = append(, )
		}
	}
	return &dialer{
		specific:    ,
		loopback:    ,
		unspecified: ,
	}
}