| 1 | //! A private parser implementation of IPv4, IPv6, and socket addresses. |
| 2 | //! |
| 3 | //! This module is "publicly exported" through the `FromStr` implementations |
| 4 | //! below. |
| 5 | |
| 6 | use crate::error::Error; |
| 7 | use crate::fmt; |
| 8 | use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; |
| 9 | use crate::str::FromStr; |
| 10 | |
| 11 | trait ReadNumberHelper: Sized { |
| 12 | const ZERO: Self; |
| 13 | fn checked_mul(&self, other: u32) -> Option<Self>; |
| 14 | fn checked_add(&self, other: u32) -> Option<Self>; |
| 15 | } |
| 16 | |
| 17 | macro_rules! impl_helper { |
| 18 | ($($t:ty)*) => ($(impl ReadNumberHelper for $t { |
| 19 | const ZERO: Self = 0; |
| 20 | #[inline] |
| 21 | fn checked_mul(&self, other: u32) -> Option<Self> { |
| 22 | Self::checked_mul(*self, other.try_into().ok()?) |
| 23 | } |
| 24 | #[inline] |
| 25 | fn checked_add(&self, other: u32) -> Option<Self> { |
| 26 | Self::checked_add(*self, other.try_into().ok()?) |
| 27 | } |
| 28 | })*) |
| 29 | } |
| 30 | |
| 31 | impl_helper! { u8 u16 u32 } |
| 32 | |
| 33 | struct Parser<'a> { |
| 34 | // Parsing as ASCII, so can use byte array. |
| 35 | state: &'a [u8], |
| 36 | } |
| 37 | |
| 38 | impl<'a> Parser<'a> { |
| 39 | fn new(input: &'a [u8]) -> Parser<'a> { |
| 40 | Parser { state: input } |
| 41 | } |
| 42 | |
| 43 | /// Run a parser, and restore the pre-parse state if it fails. |
| 44 | fn read_atomically<T, F>(&mut self, inner: F) -> Option<T> |
| 45 | where |
| 46 | F: FnOnce(&mut Parser<'_>) -> Option<T>, |
| 47 | { |
| 48 | let state = self.state; |
| 49 | let result = inner(self); |
| 50 | if result.is_none() { |
| 51 | self.state = state; |
| 52 | } |
| 53 | result |
| 54 | } |
| 55 | |
| 56 | /// Run a parser, but fail if the entire input wasn't consumed. |
| 57 | /// Doesn't run atomically. |
| 58 | fn parse_with<T, F>(&mut self, inner: F, kind: AddrKind) -> Result<T, AddrParseError> |
| 59 | where |
| 60 | F: FnOnce(&mut Parser<'_>) -> Option<T>, |
| 61 | { |
| 62 | let result = inner(self); |
| 63 | if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind)) |
| 64 | } |
| 65 | |
| 66 | /// Peek the next character from the input |
| 67 | fn peek_char(&self) -> Option<char> { |
| 68 | self.state.first().map(|&b| char::from(b)) |
| 69 | } |
| 70 | |
| 71 | /// Reads the next character from the input |
| 72 | fn read_char(&mut self) -> Option<char> { |
| 73 | self.state.split_first().map(|(&b, tail)| { |
| 74 | self.state = tail; |
| 75 | char::from(b) |
| 76 | }) |
| 77 | } |
| 78 | |
| 79 | #[must_use ] |
| 80 | /// Reads the next character from the input if it matches the target. |
| 81 | fn read_given_char(&mut self, target: char) -> Option<()> { |
| 82 | self.read_atomically(|p| { |
| 83 | p.read_char().and_then(|c| if c == target { Some(()) } else { None }) |
| 84 | }) |
| 85 | } |
| 86 | |
| 87 | /// Helper for reading separators in an indexed loop. Reads the separator |
| 88 | /// character iff index > 0, then runs the parser. When used in a loop, |
| 89 | /// the separator character will only be read on index > 0 (see |
| 90 | /// read_ipv4_addr for an example) |
| 91 | fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T> |
| 92 | where |
| 93 | F: FnOnce(&mut Parser<'_>) -> Option<T>, |
| 94 | { |
| 95 | self.read_atomically(move |p| { |
| 96 | if index > 0 { |
| 97 | p.read_given_char(sep)?; |
| 98 | } |
| 99 | inner(p) |
| 100 | }) |
| 101 | } |
| 102 | |
| 103 | // Read a number off the front of the input in the given radix, stopping |
| 104 | // at the first non-digit character or eof. Fails if the number has more |
| 105 | // digits than max_digits or if there is no number. |
| 106 | // |
| 107 | // INVARIANT: `max_digits` must be less than the number of digits that `u32` |
| 108 | // can represent. |
| 109 | fn read_number<T: ReadNumberHelper + TryFrom<u32>>( |
| 110 | &mut self, |
| 111 | radix: u32, |
| 112 | max_digits: Option<usize>, |
| 113 | allow_zero_prefix: bool, |
| 114 | ) -> Option<T> { |
| 115 | self.read_atomically(move |p| { |
| 116 | let mut digit_count = 0; |
| 117 | let has_leading_zero = p.peek_char() == Some('0' ); |
| 118 | |
| 119 | // If max_digits.is_some(), then we are parsing a `u8` or `u16` and |
| 120 | // don't need to use checked arithmetic since it fits within a `u32`. |
| 121 | let result = if let Some(max_digits) = max_digits { |
| 122 | // u32::MAX = 4_294_967_295u32, which is 10 digits long. |
| 123 | // `max_digits` must be less than 10 to not overflow a `u32`. |
| 124 | debug_assert!(max_digits < 10); |
| 125 | |
| 126 | let mut result = 0_u32; |
| 127 | while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { |
| 128 | result *= radix; |
| 129 | result += digit; |
| 130 | digit_count += 1; |
| 131 | |
| 132 | if digit_count > max_digits { |
| 133 | return None; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | result.try_into().ok() |
| 138 | } else { |
| 139 | let mut result = T::ZERO; |
| 140 | |
| 141 | while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { |
| 142 | result = result.checked_mul(radix)?; |
| 143 | result = result.checked_add(digit)?; |
| 144 | digit_count += 1; |
| 145 | } |
| 146 | |
| 147 | Some(result) |
| 148 | }; |
| 149 | |
| 150 | if digit_count == 0 { |
| 151 | None |
| 152 | } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { |
| 153 | None |
| 154 | } else { |
| 155 | result |
| 156 | } |
| 157 | }) |
| 158 | } |
| 159 | |
| 160 | /// Reads an IPv4 address. |
| 161 | fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> { |
| 162 | self.read_atomically(|p| { |
| 163 | let mut groups = [0; 4]; |
| 164 | |
| 165 | for (i, slot) in groups.iter_mut().enumerate() { |
| 166 | *slot = p.read_separator('.' , i, |p| { |
| 167 | // Disallow octal number in IP string. |
| 168 | // https://tools.ietf.org/html/rfc6943#section-3.1.1 |
| 169 | p.read_number(10, Some(3), false) |
| 170 | })?; |
| 171 | } |
| 172 | |
| 173 | Some(groups.into()) |
| 174 | }) |
| 175 | } |
| 176 | |
| 177 | /// Reads an IPv6 address. |
| 178 | fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> { |
| 179 | /// Read a chunk of an IPv6 address into `groups`. Returns the number |
| 180 | /// of groups read, along with a bool indicating if an embedded |
| 181 | /// trailing IPv4 address was read. Specifically, read a series of |
| 182 | /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional |
| 183 | /// trailing embedded IPv4 address. |
| 184 | fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { |
| 185 | let limit = groups.len(); |
| 186 | |
| 187 | for (i, slot) in groups.iter_mut().enumerate() { |
| 188 | // Try to read a trailing embedded IPv4 address. There must be |
| 189 | // at least two groups left. |
| 190 | if i < limit - 1 { |
| 191 | let ipv4 = p.read_separator(':' , i, |p| p.read_ipv4_addr()); |
| 192 | |
| 193 | if let Some(v4_addr) = ipv4 { |
| 194 | let [one, two, three, four] = v4_addr.octets(); |
| 195 | groups[i + 0] = u16::from_be_bytes([one, two]); |
| 196 | groups[i + 1] = u16::from_be_bytes([three, four]); |
| 197 | return (i + 2, true); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | let group = p.read_separator(':' , i, |p| p.read_number(16, Some(4), true)); |
| 202 | |
| 203 | match group { |
| 204 | Some(g) => *slot = g, |
| 205 | None => return (i, false), |
| 206 | } |
| 207 | } |
| 208 | (groups.len(), false) |
| 209 | } |
| 210 | |
| 211 | self.read_atomically(|p| { |
| 212 | // Read the front part of the address; either the whole thing, or up |
| 213 | // to the first :: |
| 214 | let mut head = [0; 8]; |
| 215 | let (head_size, head_ipv4) = read_groups(p, &mut head); |
| 216 | |
| 217 | if head_size == 8 { |
| 218 | return Some(head.into()); |
| 219 | } |
| 220 | |
| 221 | // IPv4 part is not allowed before `::` |
| 222 | if head_ipv4 { |
| 223 | return None; |
| 224 | } |
| 225 | |
| 226 | // Read `::` if previous code parsed less than 8 groups. |
| 227 | // `::` indicates one or more groups of 16 bits of zeros. |
| 228 | p.read_given_char(':' )?; |
| 229 | p.read_given_char(':' )?; |
| 230 | |
| 231 | // Read the back part of the address. The :: must contain at least one |
| 232 | // set of zeroes, so our max length is 7. |
| 233 | let mut tail = [0; 7]; |
| 234 | let limit = 8 - (head_size + 1); |
| 235 | let (tail_size, _) = read_groups(p, &mut tail[..limit]); |
| 236 | |
| 237 | // Concat the head and tail of the IP address |
| 238 | head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]); |
| 239 | |
| 240 | Some(head.into()) |
| 241 | }) |
| 242 | } |
| 243 | |
| 244 | /// Reads an IP address, either IPv4 or IPv6. |
| 245 | fn read_ip_addr(&mut self) -> Option<IpAddr> { |
| 246 | self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6)) |
| 247 | } |
| 248 | |
| 249 | /// Reads a `:` followed by a port in base 10. |
| 250 | fn read_port(&mut self) -> Option<u16> { |
| 251 | self.read_atomically(|p| { |
| 252 | p.read_given_char(':' )?; |
| 253 | p.read_number(10, None, true) |
| 254 | }) |
| 255 | } |
| 256 | |
| 257 | /// Reads a `%` followed by a scope ID in base 10. |
| 258 | fn read_scope_id(&mut self) -> Option<u32> { |
| 259 | self.read_atomically(|p| { |
| 260 | p.read_given_char('%' )?; |
| 261 | p.read_number(10, None, true) |
| 262 | }) |
| 263 | } |
| 264 | |
| 265 | /// Reads an IPv4 address with a port. |
| 266 | fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> { |
| 267 | self.read_atomically(|p| { |
| 268 | let ip = p.read_ipv4_addr()?; |
| 269 | let port = p.read_port()?; |
| 270 | Some(SocketAddrV4::new(ip, port)) |
| 271 | }) |
| 272 | } |
| 273 | |
| 274 | /// Reads an IPv6 address with a port. |
| 275 | fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> { |
| 276 | self.read_atomically(|p| { |
| 277 | p.read_given_char('[' )?; |
| 278 | let ip = p.read_ipv6_addr()?; |
| 279 | let scope_id = p.read_scope_id().unwrap_or(0); |
| 280 | p.read_given_char(']' )?; |
| 281 | |
| 282 | let port = p.read_port()?; |
| 283 | Some(SocketAddrV6::new(ip, port, 0, scope_id)) |
| 284 | }) |
| 285 | } |
| 286 | |
| 287 | /// Reads an IP address with a port. |
| 288 | fn read_socket_addr(&mut self) -> Option<SocketAddr> { |
| 289 | self.read_socket_addr_v4() |
| 290 | .map(SocketAddr::V4) |
| 291 | .or_else(|| self.read_socket_addr_v6().map(SocketAddr::V6)) |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | impl IpAddr { |
| 296 | /// Parse an IP address from a slice of bytes. |
| 297 | /// |
| 298 | /// ``` |
| 299 | /// #![feature(addr_parse_ascii)] |
| 300 | /// |
| 301 | /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
| 302 | /// |
| 303 | /// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); |
| 304 | /// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); |
| 305 | /// |
| 306 | /// assert_eq!(IpAddr::parse_ascii(b"127.0.0.1" ), Ok(localhost_v4)); |
| 307 | /// assert_eq!(IpAddr::parse_ascii(b"::1" ), Ok(localhost_v6)); |
| 308 | /// ``` |
| 309 | #[unstable (feature = "addr_parse_ascii" , issue = "101035" )] |
| 310 | pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { |
| 311 | Parser::new(b).parse_with(|p| p.read_ip_addr(), kind:AddrKind::Ip) |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | #[stable (feature = "ip_addr" , since = "1.7.0" )] |
| 316 | impl FromStr for IpAddr { |
| 317 | type Err = AddrParseError; |
| 318 | fn from_str(s: &str) -> Result<IpAddr, AddrParseError> { |
| 319 | Self::parse_ascii(s.as_bytes()) |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | impl Ipv4Addr { |
| 324 | /// Parse an IPv4 address from a slice of bytes. |
| 325 | /// |
| 326 | /// ``` |
| 327 | /// #![feature(addr_parse_ascii)] |
| 328 | /// |
| 329 | /// use std::net::Ipv4Addr; |
| 330 | /// |
| 331 | /// let localhost = Ipv4Addr::new(127, 0, 0, 1); |
| 332 | /// |
| 333 | /// assert_eq!(Ipv4Addr::parse_ascii(b"127.0.0.1" ), Ok(localhost)); |
| 334 | /// ``` |
| 335 | #[unstable (feature = "addr_parse_ascii" , issue = "101035" )] |
| 336 | pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { |
| 337 | // don't try to parse if too long |
| 338 | if b.len() > 15 { |
| 339 | Err(AddrParseError(AddrKind::Ipv4)) |
| 340 | } else { |
| 341 | Parser::new(b).parse_with(|p| p.read_ipv4_addr(), kind:AddrKind::Ipv4) |
| 342 | } |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | #[stable (feature = "rust1" , since = "1.0.0" )] |
| 347 | impl FromStr for Ipv4Addr { |
| 348 | type Err = AddrParseError; |
| 349 | fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> { |
| 350 | Self::parse_ascii(s.as_bytes()) |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | impl Ipv6Addr { |
| 355 | /// Parse an IPv6 address from a slice of bytes. |
| 356 | /// |
| 357 | /// ``` |
| 358 | /// #![feature(addr_parse_ascii)] |
| 359 | /// |
| 360 | /// use std::net::Ipv6Addr; |
| 361 | /// |
| 362 | /// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); |
| 363 | /// |
| 364 | /// assert_eq!(Ipv6Addr::parse_ascii(b"::1" ), Ok(localhost)); |
| 365 | /// ``` |
| 366 | #[unstable (feature = "addr_parse_ascii" , issue = "101035" )] |
| 367 | pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { |
| 368 | Parser::new(b).parse_with(|p| p.read_ipv6_addr(), kind:AddrKind::Ipv6) |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | #[stable (feature = "rust1" , since = "1.0.0" )] |
| 373 | impl FromStr for Ipv6Addr { |
| 374 | type Err = AddrParseError; |
| 375 | fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> { |
| 376 | Self::parse_ascii(s.as_bytes()) |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | impl SocketAddrV4 { |
| 381 | /// Parse an IPv4 socket address from a slice of bytes. |
| 382 | /// |
| 383 | /// ``` |
| 384 | /// #![feature(addr_parse_ascii)] |
| 385 | /// |
| 386 | /// use std::net::{Ipv4Addr, SocketAddrV4}; |
| 387 | /// |
| 388 | /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); |
| 389 | /// |
| 390 | /// assert_eq!(SocketAddrV4::parse_ascii(b"127.0.0.1:8080" ), Ok(socket)); |
| 391 | /// ``` |
| 392 | #[unstable (feature = "addr_parse_ascii" , issue = "101035" )] |
| 393 | pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { |
| 394 | Parser::new(b).parse_with(|p| p.read_socket_addr_v4(), kind:AddrKind::SocketV4) |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | #[stable (feature = "socket_addr_from_str" , since = "1.5.0" )] |
| 399 | impl FromStr for SocketAddrV4 { |
| 400 | type Err = AddrParseError; |
| 401 | fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> { |
| 402 | Self::parse_ascii(s.as_bytes()) |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | impl SocketAddrV6 { |
| 407 | /// Parse an IPv6 socket address from a slice of bytes. |
| 408 | /// |
| 409 | /// ``` |
| 410 | /// #![feature(addr_parse_ascii)] |
| 411 | /// |
| 412 | /// use std::net::{Ipv6Addr, SocketAddrV6}; |
| 413 | /// |
| 414 | /// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); |
| 415 | /// |
| 416 | /// assert_eq!(SocketAddrV6::parse_ascii(b"[2001:db8::1]:8080" ), Ok(socket)); |
| 417 | /// ``` |
| 418 | #[unstable (feature = "addr_parse_ascii" , issue = "101035" )] |
| 419 | pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { |
| 420 | Parser::new(b).parse_with(|p| p.read_socket_addr_v6(), kind:AddrKind::SocketV6) |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | #[stable (feature = "socket_addr_from_str" , since = "1.5.0" )] |
| 425 | impl FromStr for SocketAddrV6 { |
| 426 | type Err = AddrParseError; |
| 427 | fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> { |
| 428 | Self::parse_ascii(s.as_bytes()) |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | impl SocketAddr { |
| 433 | /// Parse a socket address from a slice of bytes. |
| 434 | /// |
| 435 | /// ``` |
| 436 | /// #![feature(addr_parse_ascii)] |
| 437 | /// |
| 438 | /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; |
| 439 | /// |
| 440 | /// let socket_v4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); |
| 441 | /// let socket_v6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080); |
| 442 | /// |
| 443 | /// assert_eq!(SocketAddr::parse_ascii(b"127.0.0.1:8080" ), Ok(socket_v4)); |
| 444 | /// assert_eq!(SocketAddr::parse_ascii(b"[::1]:8080" ), Ok(socket_v6)); |
| 445 | /// ``` |
| 446 | #[unstable (feature = "addr_parse_ascii" , issue = "101035" )] |
| 447 | pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { |
| 448 | Parser::new(b).parse_with(|p| p.read_socket_addr(), kind:AddrKind::Socket) |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | #[stable (feature = "rust1" , since = "1.0.0" )] |
| 453 | impl FromStr for SocketAddr { |
| 454 | type Err = AddrParseError; |
| 455 | fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> { |
| 456 | Self::parse_ascii(s.as_bytes()) |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | #[derive (Debug, Clone, PartialEq, Eq)] |
| 461 | enum AddrKind { |
| 462 | Ip, |
| 463 | Ipv4, |
| 464 | Ipv6, |
| 465 | Socket, |
| 466 | SocketV4, |
| 467 | SocketV6, |
| 468 | } |
| 469 | |
| 470 | /// An error which can be returned when parsing an IP address or a socket address. |
| 471 | /// |
| 472 | /// This error is used as the error type for the [`FromStr`] implementation for |
| 473 | /// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and |
| 474 | /// [`SocketAddrV6`]. |
| 475 | /// |
| 476 | /// # Potential causes |
| 477 | /// |
| 478 | /// `AddrParseError` may be thrown because the provided string does not parse as the given type, |
| 479 | /// often because it includes information only handled by a different address type. |
| 480 | /// |
| 481 | /// ```should_panic |
| 482 | /// use std::net::IpAddr; |
| 483 | /// let _foo: IpAddr = "127.0.0.1:8080" .parse().expect("Cannot handle the socket port" ); |
| 484 | /// ``` |
| 485 | /// |
| 486 | /// [`IpAddr`] doesn't handle the port. Use [`SocketAddr`] instead. |
| 487 | /// |
| 488 | /// ``` |
| 489 | /// use std::net::SocketAddr; |
| 490 | /// |
| 491 | /// // No problem, the `panic!` message has disappeared. |
| 492 | /// let _foo: SocketAddr = "127.0.0.1:8080" .parse().expect("unreachable panic" ); |
| 493 | /// ``` |
| 494 | #[stable (feature = "rust1" , since = "1.0.0" )] |
| 495 | #[derive (Debug, Clone, PartialEq, Eq)] |
| 496 | pub struct AddrParseError(AddrKind); |
| 497 | |
| 498 | #[stable (feature = "addr_parse_error_error" , since = "1.4.0" )] |
| 499 | impl fmt::Display for AddrParseError { |
| 500 | #[allow (deprecated, deprecated_in_future)] |
| 501 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 502 | fmt.write_str(self.description()) |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | #[stable (feature = "addr_parse_error_error" , since = "1.4.0" )] |
| 507 | impl Error for AddrParseError { |
| 508 | #[allow (deprecated)] |
| 509 | fn description(&self) -> &str { |
| 510 | match self.0 { |
| 511 | AddrKind::Ip => "invalid IP address syntax" , |
| 512 | AddrKind::Ipv4 => "invalid IPv4 address syntax" , |
| 513 | AddrKind::Ipv6 => "invalid IPv6 address syntax" , |
| 514 | AddrKind::Socket => "invalid socket address syntax" , |
| 515 | AddrKind::SocketV4 => "invalid IPv4 socket address syntax" , |
| 516 | AddrKind::SocketV6 => "invalid IPv6 socket address syntax" , |
| 517 | } |
| 518 | } |
| 519 | } |
| 520 | |