host.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  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, 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. impl<S, T> PartialEq<Host<T>> for Host<S>
  159. where
  160. S: PartialEq<T>,
  161. {
  162. fn eq(&self, other: &Host<T>) -> bool {
  163. match (self, other) {
  164. (Host::Domain(a), Host::Domain(b)) => a == b,
  165. (Host::Ipv4(a), Host::Ipv4(b)) => a == b,
  166. (Host::Ipv6(a), Host::Ipv6(b)) => a == b,
  167. (_, _) => false,
  168. }
  169. }
  170. }
  171. fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter<'_>) -> fmt::Result {
  172. let segments = addr.segments();
  173. let (compress_start, compress_end) = longest_zero_sequence(&segments);
  174. let mut i = 0;
  175. while i < 8 {
  176. if i == compress_start {
  177. f.write_str(":")?;
  178. if i == 0 {
  179. f.write_str(":")?;
  180. }
  181. if compress_end < 8 {
  182. i = compress_end;
  183. } else {
  184. break;
  185. }
  186. }
  187. write!(f, "{:x}", segments[i as usize])?;
  188. if i < 7 {
  189. f.write_str(":")?;
  190. }
  191. i += 1;
  192. }
  193. Ok(())
  194. }
  195. // https://url.spec.whatwg.org/#concept-ipv6-serializer step 2 and 3
  196. fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
  197. let mut longest = -1;
  198. let mut longest_length = -1;
  199. let mut start = -1;
  200. macro_rules! finish_sequence(
  201. ($end: expr) => {
  202. if start >= 0 {
  203. let length = $end - start;
  204. if length > longest_length {
  205. longest = start;
  206. longest_length = length;
  207. }
  208. }
  209. };
  210. );
  211. for i in 0..8 {
  212. if pieces[i as usize] == 0 {
  213. if start < 0 {
  214. start = i;
  215. }
  216. } else {
  217. finish_sequence!(i);
  218. start = -1;
  219. }
  220. }
  221. finish_sequence!(8);
  222. // https://url.spec.whatwg.org/#concept-ipv6-serializer
  223. // step 3: ignore lone zeroes
  224. if longest_length < 2 {
  225. (-1, -2)
  226. } else {
  227. (longest, longest + longest_length)
  228. }
  229. }
  230. /// <https://url.spec.whatwg.org/#ipv4-number-parser>
  231. fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
  232. let mut r = 10;
  233. if input.starts_with("0x") || input.starts_with("0X") {
  234. input = &input[2..];
  235. r = 16;
  236. } else if input.len() >= 2 && input.starts_with('0') {
  237. input = &input[1..];
  238. r = 8;
  239. }
  240. // At the moment we can't know the reason why from_str_radix fails
  241. // https://github.com/rust-lang/rust/issues/22639
  242. // So instead we check if the input looks like a real number and only return
  243. // an error when it's an overflow.
  244. let valid_number = match r {
  245. 8 => input.chars().all(|c| c >= '0' && c <= '7'),
  246. 10 => input.chars().all(|c| c >= '0' && c <= '9'),
  247. 16 => input
  248. .chars()
  249. .all(|c| (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')),
  250. _ => false,
  251. };
  252. if !valid_number {
  253. return Ok(None);
  254. }
  255. if input.is_empty() {
  256. return Ok(Some(0));
  257. }
  258. if input.starts_with('+') {
  259. return Ok(None);
  260. }
  261. match u32::from_str_radix(input, r) {
  262. Ok(number) => Ok(Some(number)),
  263. Err(_) => Err(()),
  264. }
  265. }
  266. /// <https://url.spec.whatwg.org/#concept-ipv4-parser>
  267. fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
  268. if input.is_empty() {
  269. return Ok(None);
  270. }
  271. let mut parts: Vec<&str> = input.split('.').collect();
  272. if parts.last() == Some(&"") {
  273. parts.pop();
  274. }
  275. if parts.len() > 4 {
  276. return Ok(None);
  277. }
  278. let mut numbers: Vec<u32> = Vec::new();
  279. let mut overflow = false;
  280. for part in parts {
  281. if part == "" {
  282. return Ok(None);
  283. }
  284. match parse_ipv4number(part) {
  285. Ok(Some(n)) => numbers.push(n),
  286. Ok(None) => return Ok(None),
  287. Err(()) => overflow = true,
  288. };
  289. }
  290. if overflow {
  291. return Err(ParseError::InvalidIpv4Address);
  292. }
  293. let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
  294. // Equivalent to: ipv4 >= 256 ** (4 − numbers.len())
  295. if ipv4 > u32::max_value() >> (8 * numbers.len() as u32) {
  296. return Err(ParseError::InvalidIpv4Address);
  297. }
  298. if numbers.iter().any(|x| *x > 255) {
  299. return Err(ParseError::InvalidIpv4Address);
  300. }
  301. for (counter, n) in numbers.iter().enumerate() {
  302. ipv4 += n << (8 * (3 - counter as u32))
  303. }
  304. Ok(Some(Ipv4Addr::from(ipv4)))
  305. }
  306. /// <https://url.spec.whatwg.org/#concept-ipv6-parser>
  307. fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
  308. let input = input.as_bytes();
  309. let len = input.len();
  310. let mut is_ip_v4 = false;
  311. let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
  312. let mut piece_pointer = 0;
  313. let mut compress_pointer = None;
  314. let mut i = 0;
  315. if len < 2 {
  316. return Err(ParseError::InvalidIpv6Address);
  317. }
  318. if input[0] == b':' {
  319. if input[1] != b':' {
  320. return Err(ParseError::InvalidIpv6Address);
  321. }
  322. i = 2;
  323. piece_pointer = 1;
  324. compress_pointer = Some(1);
  325. }
  326. while i < len {
  327. if piece_pointer == 8 {
  328. return Err(ParseError::InvalidIpv6Address);
  329. }
  330. if input[i] == b':' {
  331. if compress_pointer.is_some() {
  332. return Err(ParseError::InvalidIpv6Address);
  333. }
  334. i += 1;
  335. piece_pointer += 1;
  336. compress_pointer = Some(piece_pointer);
  337. continue;
  338. }
  339. let start = i;
  340. let end = cmp::min(len, start + 4);
  341. let mut value = 0u16;
  342. while i < end {
  343. match (input[i] as char).to_digit(16) {
  344. Some(digit) => {
  345. value = value * 0x10 + digit as u16;
  346. i += 1;
  347. }
  348. None => break,
  349. }
  350. }
  351. if i < len {
  352. match input[i] {
  353. b'.' => {
  354. if i == start {
  355. return Err(ParseError::InvalidIpv6Address);
  356. }
  357. i = start;
  358. if piece_pointer > 6 {
  359. return Err(ParseError::InvalidIpv6Address);
  360. }
  361. is_ip_v4 = true;
  362. }
  363. b':' => {
  364. i += 1;
  365. if i == len {
  366. return Err(ParseError::InvalidIpv6Address);
  367. }
  368. }
  369. _ => return Err(ParseError::InvalidIpv6Address),
  370. }
  371. }
  372. if is_ip_v4 {
  373. break;
  374. }
  375. pieces[piece_pointer] = value;
  376. piece_pointer += 1;
  377. }
  378. if is_ip_v4 {
  379. if piece_pointer > 6 {
  380. return Err(ParseError::InvalidIpv6Address);
  381. }
  382. let mut numbers_seen = 0;
  383. while i < len {
  384. if numbers_seen > 0 {
  385. if numbers_seen < 4 && (i < len && input[i] == b'.') {
  386. i += 1
  387. } else {
  388. return Err(ParseError::InvalidIpv6Address);
  389. }
  390. }
  391. let mut ipv4_piece = None;
  392. while i < len {
  393. let digit = match input[i] {
  394. c @ b'0'..=b'9' => c - b'0',
  395. _ => break,
  396. };
  397. match ipv4_piece {
  398. None => ipv4_piece = Some(digit as u16),
  399. Some(0) => return Err(ParseError::InvalidIpv6Address), // No leading zero
  400. Some(ref mut v) => {
  401. *v = *v * 10 + digit as u16;
  402. if *v > 255 {
  403. return Err(ParseError::InvalidIpv6Address);
  404. }
  405. }
  406. }
  407. i += 1;
  408. }
  409. pieces[piece_pointer] = if let Some(v) = ipv4_piece {
  410. pieces[piece_pointer] * 0x100 + v
  411. } else {
  412. return Err(ParseError::InvalidIpv6Address);
  413. };
  414. numbers_seen += 1;
  415. if numbers_seen == 2 || numbers_seen == 4 {
  416. piece_pointer += 1;
  417. }
  418. }
  419. if numbers_seen != 4 {
  420. return Err(ParseError::InvalidIpv6Address);
  421. }
  422. }
  423. if i < len {
  424. return Err(ParseError::InvalidIpv6Address);
  425. }
  426. match compress_pointer {
  427. Some(compress_pointer) => {
  428. let mut swaps = piece_pointer - compress_pointer;
  429. piece_pointer = 7;
  430. while swaps > 0 {
  431. pieces.swap(piece_pointer, compress_pointer + swaps - 1);
  432. swaps -= 1;
  433. piece_pointer -= 1;
  434. }
  435. }
  436. _ => {
  437. if piece_pointer != 8 {
  438. return Err(ParseError::InvalidIpv6Address);
  439. }
  440. }
  441. }
  442. Ok(Ipv6Addr::new(
  443. pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7],
  444. ))
  445. }