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