spiffe_rs/workloadapi/
addr.rs

1use 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}