<commit_before>package xds

import (
	"errors"
	"fmt"
	"net"
	"net/url"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
	envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
	envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
	envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
	envoy_tcp_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
	envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
	envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/any"
	"github.com/golang/protobuf/ptypes/wrappers"
	"github.com/hashicorp/go-hclog"

	"github.com/hashicorp/consul/agent/connect"
	"github.com/hashicorp/consul/agent/proxycfg"
	"github.com/hashicorp/consul/agent/structs"
	"github.com/hashicorp/consul/logging"
)

const (
	// TODO (freddy) Make this configurable
	TProxyOutboundPort = 15001
)

// listenersFromSnapshot returns the xDS API representation of the "listeners" in the snapshot.
func (s *Server) listenersFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
	if cfgSnap == nil {
		return nil, errors.New("nil config given")
	}

	switch cfgSnap.Kind {
	case structs.ServiceKindConnectProxy:
		return s.listenersFromSnapshotConnectProxy(cInfo, cfgSnap)
	case structs.ServiceKindTerminatingGateway:
		return s.listenersFromSnapshotGateway(cInfo, cfgSnap)
	case structs.ServiceKindMeshGateway:
		return s.listenersFromSnapshotGateway(cInfo, cfgSnap)
	case structs.ServiceKindIngressGateway:
		return s.listenersFromSnapshotGateway(cInfo, cfgSnap)
	default:
		return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind)
	}
}

// listenersFromSnapshotConnectProxy returns the "listeners" for a connect proxy service
func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
	resources := make([]proto.Message, 1)

	var err error

	// Configure inbound listener.
	resources[0], err = s.makeInboundListener(cInfo, cfgSnap, PublicListenerName)
	if err != nil {
		return nil, err
	}

	// This outboundListener is exclusively used when TransparentProxy mode is active.
	// In that situation there is a single listener where we are redirecting outbound traffic,
	// and each upstream gets a filter chain attached to that listener.
	var outboundListener *envoy_listener_v3.Listener

	if cfgSnap.Proxy.TransparentProxy {
		outboundListener = makeListener(OutboundListenerName, "127.0.0.1", TProxyOutboundPort, envoy_core_v3.TrafficDirection_OUTBOUND)
		outboundListener.FilterChains = make([]*envoy_listener_v3.FilterChain, 0)
		outboundListener.ListenerFilters = []*envoy_listener_v3.ListenerFilter{
			{
				// TODO (freddy): Hard-coded until we upgrade the go-control-plane library
				Name: "envoy.filters.listener.original_dst",
			},
		}
	}

	var hasFilterChains bool

	for id, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
		upstreamCfg := cfgSnap.ConnectProxy.UpstreamConfig[id]
		cfg := getAndModifyUpstreamConfigForListener(s.Logger, id, upstreamCfg, chain)

		// If escape hatch is present, create a listener from it and move on to the next
		if cfg.EnvoyListenerJSON != "" {
			upstreamListener, err := makeListenerFromUserConfig(cfg.EnvoyListenerJSON)
			if err != nil {
				return nil, err
			}
			resources = append(resources, upstreamListener)
			continue
		}

		// Generate the upstream listeners for when they are explicitly set with a local bind port
		if outboundListener == nil || (upstreamCfg != nil && upstreamCfg.LocalBindPort != 0) {
			address := "127.0.0.1"
			if upstreamCfg.LocalBindAddress != "" {
				address = upstreamCfg.LocalBindAddress
			}

			filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain(
				id,
				"",
				cfg.Protocol,
				upstreamCfg,
				chain,
				cfgSnap,
				nil,
			)
			if err != nil {
				return nil, err
			}

			upstreamListener := makeListener(id, address, upstreamCfg.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND)
			upstreamListener.FilterChains = []*envoy_listener_v3.FilterChain{
				filterChain,
			}
			resources = append(resources, upstreamListener)

			// Avoid creating filter chains below for upstreams that have dedicated listeners
			continue
		}

		filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain(
			id,
			"",
			cfg.Protocol,
			upstreamCfg,
			chain,
			cfgSnap,
			nil,
		)
		if err != nil {
			return nil, err
		}

		// For filter chains used by the transparent proxy, we need to match on multiple destination addresses.
		// These might be: the ClusterIP in k8s, or any of the service instance addresses.
		endpoints := cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[id]
		uniqueAddrs := make(map[string]struct{})

		for _, t := range chain.Targets {
			// Store all the possible IP addresses that might be used to dial this endpoint
			for _, e := range endpoints[t.ID] {
				if e.Service.Address != "" {
					uniqueAddrs[e.Service.Address] = struct{}{}
				}
				if e.Node.Address != "" {
					uniqueAddrs[e.Node.Address] = struct{}{}
				}

				for _, tagged := range e.Node.TaggedAddresses {
					if tagged != "" {
						uniqueAddrs[tagged] = struct{}{}
					}
				}
				for _, tagged := range e.Service.TaggedAddresses {
					if tagged.Address != "" {
						uniqueAddrs[tagged.Address] = struct{}{}
					}
				}
			}
		}

		// For every potential address we collected, create the appropriate address prefix to match on.
		// In this case we are matching on exact addresses, so the prefix is the address itself,
		// and the prefix length is based on whether it's IPv4 or IPv6.
		ranges := make([]*envoy_core_v3.CidrRange, 0)

		for addr := range uniqueAddrs {
			ip := net.ParseIP(addr)
			if ip == nil {
				continue
			}

			pfxLen := uint32(32)
			if ip.To4() == nil {
				pfxLen = 128
			}
			ranges = append(ranges, &envoy_core_v3.CidrRange{
				AddressPrefix: addr,
				PrefixLen:     &wrappers.UInt32Value{Value: pfxLen},
			})
		}
		filterChain.FilterChainMatch = &envoy_listener_v3.FilterChainMatch{
			PrefixRanges: ranges,
		}

		// Only attach the filter chain if there are addresses to match on
		if len(ranges) > 0 {
			outboundListener.FilterChains = append(outboundListener.FilterChains, filterChain)
		}
		hasFilterChains = true
	}

	// Only create the outbound listener when there are upstreams and filter chains are present
	if outboundListener != nil && hasFilterChains {
		// Filter chains are stable sorted to avoid draining if the list is provided out of order
		sort.SliceStable(outboundListener.FilterChains, func(i, j int) bool {
			return outboundListener.FilterChains[i].Name < outboundListener.FilterChains[j].Name
		})

		// Add a catch-all filter chain that acts as a TCP proxy to non-catalog destinations
		if cfgSnap.ConnectProxy.ClusterConfig == nil ||
			!cfgSnap.ConnectProxy.ClusterConfig.TransparentProxy.CatalogDestinationsOnly {

			filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain(
				"passthrough",
				OriginalDestinationClusterName,<commit_msg>Stable sort cidr ranges to match on<commit_after>package xds

import (
	"errors"
	"fmt"
	"net"
	"net/url"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
	envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
	envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
	envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
	envoy_tcp_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
	envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
	envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/any"
	"github.com/golang/protobuf/ptypes/wrappers"
	"github.com/hashicorp/go-hclog"

	"github.com/hashicorp/consul/agent/connect"
	"github.com/hashicorp/consul/agent/proxycfg"
	"github.com/hashicorp/consul/agent/structs"
	"github.com/hashicorp/consul/logging"
)

const (
	// TODO (freddy) Make this configurable
	TProxyOutboundPort = 15001
)

// listenersFromSnapshot returns the xDS API representation of the "listeners" in the snapshot.
func (s *Server) listenersFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
	if cfgSnap == nil {
		return nil, errors.New("nil config given")
	}

	switch cfgSnap.Kind {
	case structs.ServiceKindConnectProxy:
		return s.listenersFromSnapshotConnectProxy(cInfo, cfgSnap)
	case structs.ServiceKindTerminatingGateway:
		return s.listenersFromSnapshotGateway(cInfo, cfgSnap)
	case structs.ServiceKindMeshGateway:
		return s.listenersFromSnapshotGateway(cInfo, cfgSnap)
	case structs.ServiceKindIngressGateway:
		return s.listenersFromSnapshotGateway(cInfo, cfgSnap)
	default:
		return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind)
	}
}

// listenersFromSnapshotConnectProxy returns the "listeners" for a connect proxy service
func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
	resources := make([]proto.Message, 1)

	var err error

	// Configure inbound listener.
	resources[0], err = s.makeInboundListener(cInfo, cfgSnap, PublicListenerName)
	if err != nil {
		return nil, err
	}

	// This outboundListener is exclusively used when TransparentProxy mode is active.
	// In that situation there is a single listener where we are redirecting outbound traffic,
	// and each upstream gets a filter chain attached to that listener.
	var outboundListener *envoy_listener_v3.Listener

	if cfgSnap.Proxy.TransparentProxy {
		outboundListener = makeListener(OutboundListenerName, "127.0.0.1", TProxyOutboundPort, envoy_core_v3.TrafficDirection_OUTBOUND)
		outboundListener.FilterChains = make([]*envoy_listener_v3.FilterChain, 0)
		outboundListener.ListenerFilters = []*envoy_listener_v3.ListenerFilter{
			{
				// TODO (freddy): Hard-coded until we upgrade the go-control-plane library
				Name: "envoy.filters.listener.original_dst",
			},
		}
	}

	var hasFilterChains bool

	for id, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
		upstreamCfg := cfgSnap.ConnectProxy.UpstreamConfig[id]
		cfg := getAndModifyUpstreamConfigForListener(s.Logger, id, upstreamCfg, chain)

		// If escape hatch is present, create a listener from it and move on to the next
		if cfg.EnvoyListenerJSON != "" {
			upstreamListener, err := makeListenerFromUserConfig(cfg.EnvoyListenerJSON)
			if err != nil {
				return nil, err
			}
			resources = append(resources, upstreamListener)
			continue
		}

		// Generate the upstream listeners for when they are explicitly set with a local bind port
		if outboundListener == nil || (upstreamCfg != nil && upstreamCfg.LocalBindPort != 0) {
			address := "127.0.0.1"
			if upstreamCfg.LocalBindAddress != "" {
				address = upstreamCfg.LocalBindAddress
			}

			filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain(
				id,
				"",
				cfg.Protocol,
				upstreamCfg,
				chain,
				cfgSnap,
				nil,
			)
			if err != nil {
				return nil, err
			}

			upstreamListener := makeListener(id, address, upstreamCfg.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND)
			upstreamListener.FilterChains = []*envoy_listener_v3.FilterChain{
				filterChain,
			}
			resources = append(resources, upstreamListener)

			// Avoid creating filter chains below for upstreams that have dedicated listeners
			continue
		}

		filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain(
			id,
			"",
			cfg.Protocol,
			upstreamCfg,
			chain,
			cfgSnap,
			nil,
		)
		if err != nil {
			return nil, err
		}

		// For filter chains used by the transparent proxy, we need to match on multiple destination addresses.
		// These might be: the ClusterIP in k8s, or any of the service instance addresses.
		endpoints := cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[id]
		uniqueAddrs := make(map[string]struct{})

		for _, t := range chain.Targets {
			// Store all the possible IP addresses that might be used to dial this endpoint
			for _, e := range endpoints[t.ID] {
				if e.Service.Address != "" {
					uniqueAddrs[e.Service.Address] = struct{}{}
				}
				if e.Node.Address != "" {
					uniqueAddrs[e.Node.Address] = struct{}{}
				}

				for _, tagged := range e.Node.TaggedAddresses {
					if tagged != "" {
						uniqueAddrs[tagged] = struct{}{}
					}
				}
				for _, tagged := range e.Service.TaggedAddresses {
					if tagged.Address != "" {
						uniqueAddrs[tagged.Address] = struct{}{}
					}
				}
			}
		}

		// For every potential address we collected, create the appropriate address prefix to match on.
		// In this case we are matching on exact addresses, so the prefix is the address itself,
		// and the prefix length is based on whether it's IPv4 or IPv6.
		ranges := make([]*envoy_core_v3.CidrRange, 0)

		for addr := range uniqueAddrs {
			ip := net.ParseIP(addr)
			if ip == nil {
				continue
			}

			pfxLen := uint32(32)
			if ip.To4() == nil {
				pfxLen = 128
			}
			ranges = append(ranges, &envoy_core_v3.CidrRange{
				AddressPrefix: addr,
				PrefixLen:     &wrappers.UInt32Value{Value: pfxLen},
			})
		}

		// The match rules are stable sorted to avoid draining if the list is provided out of order
		sort.SliceStable(ranges, func(i, j int) bool {
			return ranges[i].AddressPrefix < ranges[j].AddressPrefix
		})

		filterChain.FilterChainMatch = &envoy_listener_v3.FilterChainMatch{
			PrefixRanges: ranges,
		}

		// Only attach the filter chain if there are addresses to match on
		if len(ranges) > 0 {
			outboundListener.FilterChains = append(outboundListener.FilterChains, filterChain)
		}
		hasFilterChains = true
	}

	// Only create the outbound listener when there are upstreams and filter chains are present
	if outboundListener != nil && hasFilterChains {
		// Filter chains are stable sorted to avoid draining if the list is provided out of order
		sort.SliceStable(outboundListener.FilterChains, func(i, j int) bool {
			return outboundListener.FilterChains[i].Name < outboundListener.FilterChains[j].Name
		})

		// Add a catch-all filter chain that acts as a TCP proxy to non-catalog destinations
		if cfgSnap.ConnectProxy.ClusterConfig == nil ||
			!cfgSnap.ConnectProxy.ClusterConfig.TransparentProxy.CatalogDestinationsOnly {

			filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain(
				"passthrough",
				OriginalDestinationClusterName,
{'fraction_non_alphanumeric': 0.10101144530210274, 'fraction_numerical': 0.007852009582113389, 'mean_word_length': 4.091124661246613, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3587399', 'n_tokens_mistral': 5453, 'n_tokens_neox': 4816, 'n_words': 1166}