1use std::net::SocketAddr;
16use std::sync::Arc;
17
18use eggress_core::{ClientIdentity, TargetAddr, TargetHost};
19use eggress_protocol_reverse::client::{TargetResolution, TargetResolver};
20use eggress_routing::{
21 RouteDecision, RouteRequest, RouteService, SharedRoutingService, TransportKind,
22};
23use tracing::warn;
24
25pub struct RouteEngineTargetResolver {
37 router: Arc<SharedRoutingService>,
38 target: TargetAddr,
39 reverse_listener: Arc<str>,
40 client_addr: Option<SocketAddr>,
41}
42
43impl RouteEngineTargetResolver {
44 pub fn new(
46 router: Arc<SharedRoutingService>,
47 host: String,
48 port: u16,
49 reverse_listener: Arc<str>,
50 client_addr: Option<SocketAddr>,
51 ) -> Self {
52 let target = TargetAddr {
53 host: TargetHost::Domain(host),
54 port,
55 };
56 Self {
57 router,
58 target,
59 reverse_listener,
60 client_addr,
61 }
62 }
63
64 pub fn target(&self) -> &TargetAddr {
67 &self.target
68 }
69}
70
71impl TargetResolver for RouteEngineTargetResolver {
72 fn resolve(&self) -> TargetResolution {
73 let request = RouteRequest {
74 target: &self.target,
75 source: self.client_addr,
76 listener: self.reverse_listener.as_ref(),
77 inbound_protocol: eggress_core::ProtocolId::Reverse,
78 identity: &ClientIdentity::Anonymous,
79 transport: TransportKind::ReverseTcp,
80 };
81
82 match self.router.decide(&request) {
83 RouteDecision::Direct { .. } | RouteDecision::UpstreamGroup { .. } => {
84 TargetResolution::Connect {
85 host: self.target.host.to_string(),
86 port: self.target.port,
87 }
88 }
89 RouteDecision::Reject { reason, .. } => {
90 warn!(
91 listener = %self.reverse_listener,
92 target = %self.target,
93 ?reason,
94 "reverse route rejected by policy",
95 );
96 TargetResolution::Reject {
97 reason: format!("route rejected: {:?}", reason),
98 }
99 }
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use eggress_core::RejectReason;
108 use eggress_routing::{CompiledRule, RouteActionSpec, Router, RuleId, SharedRoutingService};
109
110 fn router_with_default(action: RouteActionSpec) -> Arc<SharedRoutingService> {
111 Arc::new(SharedRoutingService::new(Router::new(Vec::new(), action)))
112 }
113
114 #[test]
115 fn allows_direct_route() {
116 let r = router_with_default(RouteActionSpec::Direct);
117 let resolver = RouteEngineTargetResolver::new(
118 r,
119 "127.0.0.1".to_string(),
120 8080,
121 Arc::from("rev-1"),
122 None,
123 );
124 match resolver.resolve() {
125 TargetResolution::Connect { host, port } => {
126 assert_eq!(host, "127.0.0.1");
127 assert_eq!(port, 8080);
128 }
129 other => panic!("expected Connect, got {:?}", other),
130 }
131 }
132
133 #[test]
134 fn allows_upstream_group_route() {
135 let r = router_with_default(RouteActionSpec::UpstreamGroup(
136 eggress_routing::UpstreamGroupId(Arc::from("group-1")),
137 ));
138 let resolver = RouteEngineTargetResolver::new(
139 r,
140 "127.0.0.1".to_string(),
141 9090,
142 Arc::from("rev-1"),
143 None,
144 );
145 match resolver.resolve() {
148 TargetResolution::Connect { port, .. } => assert_eq!(port, 9090),
149 other => panic!("expected Connect, got {:?}", other),
150 }
151 }
152
153 #[test]
154 fn rejects_reject_route() {
155 let rule = CompiledRule {
156 id: RuleId(Arc::from("deny-all")),
157 matcher: eggress_routing::MatchExpr::Any,
158 action: RouteActionSpec::Reject(RejectReason::AccessDenied),
159 };
160 let router = Arc::new(SharedRoutingService::new(Router::new(
161 vec![rule],
162 RouteActionSpec::Direct,
163 )));
164 let resolver = RouteEngineTargetResolver::new(
165 router,
166 "127.0.0.1".to_string(),
167 80,
168 Arc::from("rev-1"),
169 None,
170 );
171 match resolver.resolve() {
172 TargetResolution::Reject { reason } => {
173 assert!(reason.contains("route rejected"), "reason: {}", reason);
174 }
175 other => panic!("expected Reject, got {:?}", other),
176 }
177 }
178
179 #[test]
180 fn reverse_listener_matcher_only_matches_reverse_tcp() {
181 let rule = CompiledRule {
183 id: RuleId(Arc::from("reverse-only")),
184 matcher: eggress_routing::MatchExpr::ReverseListener(Arc::from("rev-1")),
185 action: RouteActionSpec::Direct,
186 };
187 let router = Arc::new(SharedRoutingService::new(Router::new(
188 vec![rule],
189 RouteActionSpec::Reject(RejectReason::AccessDenied),
190 )));
191 let resolver = RouteEngineTargetResolver::new(
192 router,
193 "127.0.0.1".to_string(),
194 8080,
195 Arc::from("rev-1"),
196 None,
197 );
198 assert!(matches!(
200 resolver.resolve(),
201 TargetResolution::Connect { .. }
202 ));
203 }
204}