host.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. // Copyright 2013-2016 The rust-url developers.
  2. //
  3. // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
  4. // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
  5. // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
  6. // option. This file may not be copied, modified, or distributed
  7. // except according to those terms.
  8. use std::cmp;
  9. use std::fmt::{self, Formatter};
  10. use std::net::{Ipv4Addr, Ipv6Addr};
  11. use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
  12. #[cfg(feature = "serde")]
  13. use serde::{Deserialize, Serialize};
  14. use crate::parser::{ParseError, ParseResult};
  15. #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
  16. #[derive(Copy, Clone, Debug, Eq, PartialEq)]
  17. pub(crate) enum HostInternal {
  18. None,
  19. Domain,
  20. Ipv4(Ipv4Addr),
  21. Ipv6(Ipv6Addr),
  22. }
  23. impl From<Host<String>> for HostInternal {
  24. fn from(host: Host<String>) -> HostInternal {
  25. match host {
  26. Host::Domain(ref s) if s.is_empty() => HostInternal::None,
  27. Host::Domain(_) => HostInternal::Domain,
  28. Host::Ipv4(address) => HostInternal::Ipv4(address),
  29. Host::Ipv6(address) => HostInternal::Ipv6(address),
  30. }
  31. }
  32. }
  33. /// The host name of an URL.
  34. #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
  35. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
  36. pub enum Host<S = String> {
  37. /// A DNS domain name, as '.' dot-separated labels.
  38. /// Non-ASCII labels are encoded in punycode per IDNA if this is the host of
  39. /// a special URL, or percent encoded for non-special URLs. Hosts for
  40. /// non-special URLs are also called opaque hosts.
  41. Domain(S),
  42. /// An IPv4 address.
  43. /// `Url::host_str` returns the serialization of this address,
  44. /// as four decimal integers separated by `.` dots.
  45. Ipv4(Ipv4Addr),
  46. /// An IPv6 address.
  47. /// `Url::host_str` returns the serialization of that address between `[` and `]` brackets,
  48. /// in the format per [RFC 5952 *A Recommendation
  49. /// for IPv6 Address Text Representation*](https://tools.ietf.org/html/rfc5952):
  50. /// lowercase hexadecimal with maximal `::` compression.
  51. Ipv6(Ipv6Addr),
  52. }
  53. impl<'a> Host<&'a str> {
  54. /// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.
  55. pub fn to_owned(&self) -> Host<String> {
  56. match *self {
  57. Host::Domain(domain) => Host::Domain(domain.to_owned()),
  58. Host::Ipv4(address) => Host::Ipv4(address),
  59. Host::Ipv6(address) => Host::Ipv6(address),
  60. }
  61. }
  62. }
  63. impl Host<String> {
  64. /// Parse a host: either an IPv6 address in [] square brackets, or a domain.
  65. ///
  66. /// <https://url.spec.whatwg.org/#host-parsing>
  67. pub fn parse(input: &str) -> Result<Self, ParseError> {
  68. if input.starts_with('[') {
  69. if !input.ends_with(']') {
  70. return Err(ParseError::InvalidIpv6Address);
  71. }
  72. return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
  73. }
  74. let domain = percent_decode(input.as_bytes()).decode_utf8_lossy();
  75. let domain = idna::domain_to_ascii(&domain)?;
  76. if domain.is_empty() {
  77. return Err(ParseError::EmptyHost);
  78. }
  79. let is_invalid_domain_char = |c| {
  80. matches!(
  81. c,
  82. '\0' | '\t'
  83. | '\n'
  84. | '\r'
  85. | ' '
  86. | '#'
  87. | '%'
  88. | '/'
  89. | ':'
  90. | '<'
  91. | '>'
  92. | '?'
  93. | '@'
  94. | '['
  95. | '\\'
  96. | ']'
  97. | '^'
  98. )
  99. };
  100. if domain.find(is_invalid_domain_char).is_some() {
  101. Err(ParseError::InvalidDomainCharacter)
  102. } else if let Some(address) = parse_ipv4addr(&domain)? {
  103. Ok(Host::Ipv4(address))
  104. } else {
  105. Ok(Host::Domain(domain))
  106. }
  107. }
  108. // <https://url.spec.whatwg.org/#concept-opaque-host-parser>
  109. pub fn parse_opaque(input: &str) -> Result<Self, ParseError> {
  110. if input.starts_with('[') {
  111. if !input.ends_with(']') {
  112. return Err(ParseError::InvalidIpv6Address);
  113. }
  114. return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
  115. }
  116. let is_invalid_host_char = |c| {
  117. matches!(
  118. c,
  119. '\0' | '\t'
  120. | '\n'
  121. | '\r'
  122. | ' '
  123. | '#'
  124. | '/'
  125. | ':'
  126. | '<'
  127. | '>'
  128. | '?'
  129. | '@'
  130. | '['
  131. | '\\'
  132. | ']'
  133. | '^'
  134. )
  135. };
  136. if input.find(is_invalid_host_char).is_some() {
  137. Err(ParseError::InvalidDomainCharacter)
  138. } else {
  139. Ok(Host::Domain(
  140. utf8_percent_encode(input, CONTROLS).to_string(),
  141. ))
  142. }
  143. }
  144. }
  145. impl<S: AsRef<str>> fmt::Display for Host<S> {
  146. fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
  147. match *self {
  148. Host::Domain(ref domain) => domain.as_ref().fmt(f),
  149. Host::Ipv4(ref addr) => addr.fmt(f),
  150. Host::Ipv6(ref addr) => {
  151. f.write_str("[")?;
  152. write_ipv6(addr, f)?;
  153. f.write_str("]")
  154. }
  155. }
  156. }
  157. }
  158. fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter<'_>) -> fmt::Result {
  159. let segments = addr.segments();
  160. let (compress_start, compress_end) = longest_zero_sequence(&segments);
  161. let mut i = 0;
  162. while i < 8 {
  163. if i == compress_start {
  164. f.write_str(":")?;
  165. if i == 0 {
  166. f.write_str(":")?;
  167. }
  168. if compress_end < 8 {
  169. i = compress_end;
  170. } else {
  171. break;
  172. }
  173. }
  174. write!(f, "{:x}", segments[i as usize])?;
  175. if i < 7 {
  176. f.write_str(":")?;
  177. }
  178. i += 1;
  179. }
  180. Ok(())
  181. }
  182. // https://url.spec.whatwg.org/#concept-ipv6-serializer step 2 and 3
  183. fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
  184. let mut longest = -1;
  185. let mut longest_length = -1;
  186. let mut start = -1;
  187. macro_rules! finish_sequence(
  188. ($end: expr) => {
  189. if start >= 0 {
  190. let length = $end - start;
  191. if length > longest_length {
  192. longest = start;
  193. longest_length = length;
  194. }
  195. }
  196. };
  197. );
  198. for i in 0..8 {
  199. if pieces[i as usize] == 0 {
  200. if start < 0 {
  201. start = i;
  202. }
  203. } else {
  204. finish_sequence!(i);
  205. start = -1;
  206. }
  207. }
  208. finish_sequence!(8);
  209. // https://url.spec.whatwg.org/#concept-ipv6-serializer
  210. // step 3: ignore lone zeroes
  211. if longest_length < 2 {
  212. (-1, -2)
  213. } else {
  214. (longest, longest + longest_length)
  215. }
  216. }
  217. /// <https://url.spec.whatwg.org/#ipv4-number-parser>
  218. fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
  219. let mut r = 10;
  220. if input.starts_with("0x") || input.starts_with("0X") {
  221. input = &input[2..];
  222. r = 16;
  223. } else if input.len() >= 2 && input.starts_with('0') {
  224. input = &input[1..];
  225. r = 8;
  226. }
  227. // At the moment we can't know the reason why from_str_radix fails
  228. // https://github.com/rust-lang/rust/issues/22639
  229. // So instead we check if the input looks like a real number and only return
  230. // an error when it's an overflow.
  231. let valid_number = match r {
  232. 8 => input.chars().all(|c| c >= '0' && c <= '7'),
  233. 10 => input.chars().all(|c| c >= '0' && c <= '9'),
  234. 16 => input
  235. .chars()
  236. .all(|c| (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')),
  237. _ => false,
  238. };
  239. if !valid_number {
  240. return Ok(None);
  241. }
  242. if input.is_empty() {
  243. return Ok(Some(0));
  244. }
  245. if input.starts_with('+') {
  246. return Ok(None);
  247. }
  248. match u32::from_str_radix(input, r) {
  249. Ok(number) => Ok(Some(number)),
  250. Err(_) => Err(()),
  251. }
  252. }
  253. /// <https://url.spec.whatwg.org/#concept-ipv4-parser>
  254. fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
  255. if input.is_empty() {
  256. return Ok(None);
  257. }
  258. let mut parts: Vec<&str> = input.split('.').collect();
  259. if parts.last() == Some(&"") {
  260. parts.pop();
  261. }
  262. if parts.len() > 4 {
  263. return Ok(None);
  264. }
  265. let mut numbers: Vec<u32> = Vec::new();
  266. let mut overflow = false;
  267. for part in parts {
  268. if part == "" {
  269. return Ok(None);
  270. }
  271. match parse_ipv4number(part) {
  272. Ok(Some(n)) => numbers.push(n),
  273. Ok(None) => return Ok(None),
  274. Err(()) => overflow = true,
  275. };
  276. }
  277. if overflow {
  278. return Err(ParseError::InvalidIpv4Address);
  279. }
  280. let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
  281. // Equivalent to: ipv4 >= 256 ** (4 − numbers.len())
  282. if ipv4 > u32::max_value() >> (8 * numbers.len() as u32) {
  283. return Err(ParseError::InvalidIpv4Address);
  284. }
  285. if numbers.iter().any(|x| *x > 255) {
  286. return Err(ParseError::InvalidIpv4Address);
  287. }
  288. for (counter, n) in numbers.iter().enumerate() {
  289. ipv4 += n << (8 * (3 - counter as u32))
  290. }
  291. Ok(Some(Ipv4Addr::from(ipv4)))
  292. }
  293. /// <https://url.spec.whatwg.org/#concept-ipv6-parser>
  294. fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
  295. let input = input.as_bytes();
  296. let len = input.len();
  297. let mut is_ip_v4 = false;
  298. let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
  299. let mut piece_pointer = 0;
  300. let mut compress_pointer = None;
  301. let mut i = 0;
  302. if len < 2 {
  303. return Err(ParseError::InvalidIpv6Address);
  304. }
  305. if input[0] == b':' {
  306. if input[1] != b':' {
  307. return Err(ParseError::InvalidIpv6Address);
  308. }
  309. i = 2;
  310. piece_pointer = 1;
  311. compress_pointer = Some(1);
  312. }
  313. while i < len {
  314. if piece_pointer == 8 {
  315. return Err(ParseError::InvalidIpv6Address);
  316. }
  317. if input[i] == b':' {
  318. if compress_pointer.is_some() {
  319. return Err(ParseError::InvalidIpv6Address);
  320. }
  321. i += 1;
  322. piece_pointer += 1;
  323. compress_pointer = Some(piece_pointer);
  324. continue;
  325. }
  326. let start = i;
  327. let end = cmp::min(len, start + 4);
  328. let mut value = 0u16;
  329. while i < end {
  330. match (input[i] as char).to_digit(16) {
  331. Some(digit) => {
  332. value = value * 0x10 + digit as u16;
  333. i += 1;
  334. }
  335. None => break,
  336. }
  337. }
  338. if i < len {
  339. match input[i] {
  340. b'.' => {
  341. if i == start {
  342. return Err(ParseError::InvalidIpv6Address);
  343. }
  344. i = start;
  345. if piece_pointer > 6 {
  346. return Err(ParseError::InvalidIpv6Address);
  347. }
  348. is_ip_v4 = true;
  349. }
  350. b':' => {
  351. i += 1;
  352. if i == len {
  353. return Err(ParseError::InvalidIpv6Address);
  354. }
  355. }
  356. _ => return Err(ParseError::InvalidIpv6Address),
  357. }
  358. }
  359. if is_ip_v4 {
  360. break;
  361. }
  362. pieces[piece_pointer] = value;
  363. piece_pointer += 1;
  364. }
  365. if is_ip_v4 {
  366. if piece_pointer > 6 {
  367. return Err(ParseError::InvalidIpv6Address);
  368. }
  369. let mut numbers_seen = 0;
  370. while i < len {
  371. if numbers_seen > 0 {
  372. if numbers_seen < 4 && (i < len && input[i] == b'.') {
  373. i += 1
  374. } else {
  375. return Err(ParseError::InvalidIpv6Address);
  376. }
  377. }
  378. let mut ipv4_piece = None;
  379. while i < len {
  380. let digit = match input[i] {
  381. c @ b'0'..=b'9' => c - b'0',
  382. _ => break,
  383. };
  384. match ipv4_piece {
  385. None => ipv4_piece = Some(digit as u16),
  386. Some(0) => return Err(ParseError::InvalidIpv6Address), // No leading zero
  387. Some(ref mut v) => {
  388. *v = *v * 10 + digit as u16;
  389. if *v > 255 {
  390. return Err(ParseError::InvalidIpv6Address);
  391. }
  392. }
  393. }
  394. i += 1;
  395. }
  396. pieces[piece_pointer] = if let Some(v) = ipv4_piece {
  397. pieces[piece_pointer] * 0x100 + v
  398. } else {
  399. return Err(ParseError::InvalidIpv6Address);
  400. };
  401. numbers_seen += 1;
  402. if numbers_seen == 2 || numbers_seen == 4 {
  403. piece_pointer += 1;
  404. }
  405. }
  406. if numbers_seen != 4 {
  407. return Err(ParseError::InvalidIpv6Address);
  408. }
  409. }
  410. if i < len {
  411. return Err(ParseError::InvalidIpv6Address);
  412. }
  413. match compress_pointer {
  414. Some(compress_pointer) => {
  415. let mut swaps = piece_pointer - compress_pointer;
  416. piece_pointer = 7;
  417. while swaps > 0 {
  418. pieces.swap(piece_pointer, compress_pointer + swaps - 1);
  419. swaps -= 1;
  420. piece_pointer -= 1;
  421. }
  422. }
  423. _ => {
  424. if piece_pointer != 8 {
  425. return Err(ParseError::InvalidIpv6Address);
  426. }
  427. }
  428. }
  429. Ok(Ipv6Addr::new(
  430. pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7],
  431. ))
  432. }