spiffe_rs/workloadapi/
addr.rs1use crate::workloadapi::{wrap_error, Result};
2use std::net::IpAddr;
3use std::env;
4use url::Url;
5
6#[allow(non_upper_case_globals)]
7pub const SocketEnv: &str = "SPIFFE_ENDPOINT_SOCKET";
8
9pub fn get_default_address() -> Option<String> {
10 env::var(SocketEnv).ok()
11}
12
13pub fn validate_address(addr: &str) -> Result<()> {
14 target_from_address(addr).map(|_| ())
15}
16
17pub fn target_from_address(addr: &str) -> Result<String> {
18 if addr.chars().any(|c| c.is_control()) {
19 return Err(wrap_error(
20 "workload endpoint socket is not a valid URI: invalid control character in URL",
21 ));
22 }
23 let url = match Url::parse(addr) {
24 Ok(url) => url,
25 Err(url::ParseError::RelativeUrlWithoutBase) => {
26 return Err(wrap_error(
27 "workload endpoint socket URI must have a \"tcp\" or \"unix\" scheme",
28 ));
29 }
30 Err(err) => {
31 return Err(wrap_error(format!(
32 "workload endpoint socket is not a valid URI: {}",
33 err
34 )));
35 }
36 };
37 parse_target_from_url(&url)
38}
39
40fn parse_target_from_url(url: &Url) -> Result<String> {
41 if url.scheme() == "tcp" {
42 if url.cannot_be_a_base() {
43 return Err(wrap_error(
44 "workload endpoint tcp socket URI must not be opaque",
45 ));
46 }
47 if !url.username().is_empty() || url.password().is_some() {
48 return Err(wrap_error(
49 "workload endpoint tcp socket URI must not include user info",
50 ));
51 }
52 if url.host_str().is_none() {
53 return Err(wrap_error(
54 "workload endpoint tcp socket URI must include a host",
55 ));
56 }
57 if !url.path().is_empty() && url.path() != "/" {
58 return Err(wrap_error(
59 "workload endpoint tcp socket URI must not include a path",
60 ));
61 }
62 if url.query().is_some() {
63 return Err(wrap_error(
64 "workload endpoint tcp socket URI must not include query values",
65 ));
66 }
67 if url.fragment().is_some() {
68 return Err(wrap_error(
69 "workload endpoint tcp socket URI must not include a fragment",
70 ));
71 }
72 let host = url
73 .host_str()
74 .ok_or_else(|| wrap_error("workload endpoint tcp socket URI must include a host"))?;
75 let ip: IpAddr = host
76 .parse()
77 .map_err(|_| wrap_error("workload endpoint tcp socket URI host component must be an IP:port"))?;
78 let port = url
79 .port()
80 .ok_or_else(|| wrap_error("workload endpoint tcp socket URI host component must include a port"))?;
81 return Ok(format!("{}:{}", ip, port));
82 }
83
84 parse_target_from_url_os(url)
85}
86
87fn parse_target_from_url_os(url: &Url) -> Result<String> {
88 match url.scheme() {
89 "unix" => {
90 if url.cannot_be_a_base() {
91 return Err(wrap_error(
92 "workload endpoint unix socket URI must not be opaque",
93 ));
94 }
95 if !url.username().is_empty() || url.password().is_some() {
96 return Err(wrap_error(
97 "workload endpoint unix socket URI must not include user info",
98 ));
99 }
100 if url.host_str().unwrap_or("").is_empty() && url.path().is_empty() {
101 return Err(wrap_error(
102 "workload endpoint unix socket URI must include a path",
103 ));
104 }
105 if url.query().is_some() {
106 return Err(wrap_error(
107 "workload endpoint unix socket URI must not include query values",
108 ));
109 }
110 if url.fragment().is_some() {
111 return Err(wrap_error(
112 "workload endpoint unix socket URI must not include a fragment",
113 ));
114 }
115 Ok(url.to_string())
116 }
117 _ => Err(wrap_error(
118 "workload endpoint socket URI must have a \"tcp\" or \"unix\" scheme",
119 )),
120 }
121}