steamkit/steam/discovery/
server_record.rs1use std::fmt;
13use std::hash::{Hash, Hasher};
14use std::net::{IpAddr, SocketAddr};
15
16use crate::error::SteamError;
17use crate::networking::steam3::protocol_type::ProtocolTypes;
18
19#[derive(Clone, Debug)]
21pub enum EndPoint {
22 Ip(SocketAddr),
24 Dns {
26 host: String,
28 port: u16,
30 address: SocketAddr,
32 },
33}
34
35impl EndPoint {
36 pub fn dns(host: impl Into<String>, port: u16, address: SocketAddr) -> Self {
39 EndPoint::Dns {
40 host: host.into(),
41 port,
42 address,
43 }
44 }
45
46 pub fn socket_addr(&self) -> SocketAddr {
48 match self {
49 EndPoint::Ip(address) => *address,
50 EndPoint::Dns { address, .. } => *address,
51 }
52 }
53
54 pub fn host(&self) -> String {
56 match self {
57 EndPoint::Ip(address) => address.ip().to_string(),
58 EndPoint::Dns { host, .. } => host.clone(),
59 }
60 }
61
62 pub fn port(&self) -> u16 {
64 match self {
65 EndPoint::Ip(address) => address.port(),
66 EndPoint::Dns { port, .. } => *port,
67 }
68 }
69}
70
71impl PartialEq for EndPoint {
72 fn eq(&self, other: &Self) -> bool {
75 match (self, other) {
76 (EndPoint::Ip(left), EndPoint::Ip(right)) => left == right,
77 (
78 EndPoint::Dns {
79 host: left_host,
80 port: left_port,
81 ..
82 },
83 EndPoint::Dns {
84 host: right_host,
85 port: right_port,
86 ..
87 },
88 ) => left_port == right_port && left_host.eq_ignore_ascii_case(right_host),
89 _ => false,
90 }
91 }
92}
93
94impl Eq for EndPoint {}
95
96impl Hash for EndPoint {
97 fn hash<H: Hasher>(&self, state: &mut H) {
98 match self {
99 EndPoint::Ip(address) => {
100 0u8.hash(state);
101 address.hash(state);
102 }
103 EndPoint::Dns { host, port, .. } => {
104 1u8.hash(state);
105 port.hash(state);
106 host.to_ascii_lowercase().hash(state);
107 }
108 }
109 }
110}
111
112impl fmt::Display for EndPoint {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self {
115 EndPoint::Ip(address) => write!(f, "{address}"),
116 EndPoint::Dns { host, port, .. } => write!(f, "{host}:{port}"),
117 }
118 }
119}
120
121impl From<SocketAddr> for EndPoint {
122 fn from(address: SocketAddr) -> Self {
123 EndPoint::Ip(address)
124 }
125}
126
127#[derive(Clone, Debug)]
129pub struct ServerRecord {
130 end_point: EndPoint,
131 protocol_types: ProtocolTypes,
132}
133
134impl ServerRecord {
135 pub fn new(end_point: EndPoint, protocol_types: ProtocolTypes) -> Self {
137 ServerRecord {
138 end_point,
139 protocol_types,
140 }
141 }
142
143 pub fn end_point(&self) -> SocketAddr {
146 self.end_point.socket_addr()
147 }
148
149 pub fn endpoint(&self) -> &EndPoint {
151 &self.end_point
152 }
153
154 pub fn is_dns(&self) -> bool {
156 matches!(self.end_point, EndPoint::Dns { .. })
157 }
158
159 pub fn protocol_types(&self) -> ProtocolTypes {
161 self.protocol_types
162 }
163
164 pub fn get_host(&self) -> String {
166 self.end_point.host()
167 }
168
169 pub fn get_port(&self) -> u16 {
171 self.end_point.port()
172 }
173
174 pub async fn create_server(
177 host: &str,
178 port: u16,
179 protocol_types: ProtocolTypes,
180 ) -> Result<Self, SteamError> {
181 match host.parse::<IpAddr>() {
182 Ok(address) => Ok(ServerRecord::new(
183 EndPoint::Ip(SocketAddr::new(address, port)),
184 protocol_types,
185 )),
186 Err(_) => {
187 let address = resolve_host(host, port).await?;
188 Ok(ServerRecord::new(
189 EndPoint::dns(host, port, address),
190 protocol_types,
191 ))
192 }
193 }
194 }
195
196 pub fn create_socket_server(end_point: SocketAddr) -> Self {
198 ServerRecord::new(EndPoint::Ip(end_point), tcp_udp_protocol_types())
199 }
200
201 pub fn try_create_socket_server(address: &str) -> Option<Self> {
204 let end_point = address.parse::<SocketAddr>().ok()?;
205 if end_point.port() == 0 {
206 return None;
207 }
208
209 Some(ServerRecord::new(
210 EndPoint::Ip(end_point),
211 tcp_udp_protocol_types(),
212 ))
213 }
214
215 pub async fn create_dns_socket_server(address: &str) -> Result<Self, SteamError> {
218 ServerRecord::create_server_from_dns(address, tcp_udp_protocol_types()).await
219 }
220
221 pub async fn create_websocket_server(address: &str) -> Result<Self, SteamError> {
224 ServerRecord::create_server_from_dns(address, ProtocolTypes::WEB_SOCKET).await
225 }
226
227 async fn create_server_from_dns(
229 address: &str,
230 protocol_types: ProtocolTypes,
231 ) -> Result<Self, SteamError> {
232 const DEFAULT_PORT: u16 = 443;
233
234 let (host, port) = match address.find(':') {
235 Some(index) => {
236 let host = &address[..index];
237 let port = &address[index + 1..];
238 let port = port.parse::<u16>().map_err(|_| {
239 SteamError::InvalidArgument(
240 "Port number must be a valid integer value.".to_string(),
241 )
242 })?;
243 (host, port)
244 }
245 None => (address, DEFAULT_PORT),
246 };
247
248 let resolved = resolve_host(host, port).await?;
249
250 Ok(ServerRecord::new(
251 EndPoint::dns(host, port, resolved),
252 protocol_types,
253 ))
254 }
255}
256
257impl PartialEq for ServerRecord {
258 fn eq(&self, other: &Self) -> bool {
260 self.end_point == other.end_point && self.protocol_types == other.protocol_types
261 }
262}
263
264impl Eq for ServerRecord {}
265
266impl Hash for ServerRecord {
267 fn hash<H: Hasher>(&self, state: &mut H) {
268 self.end_point.hash(state);
269 self.protocol_types.hash(state);
270 }
271}
272
273impl fmt::Display for ServerRecord {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 write!(f, "{} ({:?})", self.end_point, self.protocol_types)
276 }
277}
278
279async fn resolve_host(host: &str, port: u16) -> Result<SocketAddr, SteamError> {
281 let mut addresses = tokio::net::lookup_host((host, port))
282 .await
283 .map_err(|error| SteamError::Connection(format!("Failed to resolve {host}: {error}")))?;
284
285 addresses.next().ok_or_else(|| {
286 SteamError::Connection(format!("Failed to resolve {host}: no addresses returned"))
287 })
288}
289
290pub(crate) fn tcp_udp_protocol_types() -> ProtocolTypes {
292 ProtocolTypes(ProtocolTypes::TCP.raw() | ProtocolTypes::UDP.raw())
293}