host.rs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // Copyright 2013-2014 Simon Sapin.
  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::ascii::AsciiExt;
  9. use std::cmp;
  10. use std::fmt::{self, Formatter};
  11. use std::net::{Ipv4Addr, Ipv6Addr};
  12. use parser::{ParseResult, ParseError};
  13. use percent_encoding::{from_hex, percent_decode};
  14. /// The host name of an URL.
  15. #[derive(PartialEq, Eq, Clone, Debug, Hash, PartialOrd, Ord)]
  16. #[cfg_attr(feature="heap_size", derive(HeapSizeOf))]
  17. pub enum Host {
  18. /// A (DNS) domain name.
  19. Domain(String),
  20. /// A IPv4 address, represented by four sequences of up to three ASCII digits.
  21. Ipv4(Ipv4Addr),
  22. /// An IPv6 address, represented inside `[...]` square brackets
  23. /// so that `:` colon characters in the address are not ambiguous
  24. /// with the port number delimiter.
  25. Ipv6(Ipv6Addr),
  26. }
  27. impl Host {
  28. /// Parse a host: either an IPv6 address in [] square brackets, or a domain.
  29. ///
  30. /// Returns `Err` for an empty host, an invalid IPv6 address,
  31. /// or a or invalid non-ASCII domain.
  32. ///
  33. /// FIXME: Add IDNA support for non-ASCII domains.
  34. pub fn parse(input: &str) -> ParseResult<Host> {
  35. if input.len() == 0 {
  36. return Err(ParseError::EmptyHost)
  37. }
  38. if input.starts_with("[") {
  39. if !input.ends_with("]") {
  40. return Err(ParseError::InvalidIpv6Address)
  41. }
  42. return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6)
  43. }
  44. let decoded = percent_decode(input.as_bytes());
  45. let domain = String::from_utf8_lossy(&decoded);
  46. // TODO: Remove this check and use IDNA "domain to ASCII"
  47. if !domain.is_ascii() {
  48. return Err(ParseError::NonAsciiDomainsNotSupportedYet)
  49. } else if domain.find(&[
  50. '\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
  51. ][..]).is_some() {
  52. return Err(ParseError::InvalidDomainCharacter)
  53. }
  54. match parse_ipv4addr(&domain[..]) {
  55. Ok(Some(ipv4addr)) => Ok(Host::Ipv4(ipv4addr)),
  56. Ok(None) => Ok(Host::Domain(domain.to_ascii_lowercase())),
  57. Err(e) => Err(e),
  58. }
  59. }
  60. /// Serialize the host as a string.
  61. ///
  62. /// A domain a returned as-is, an IPv6 address between [] square brackets.
  63. pub fn serialize(&self) -> String {
  64. self.to_string()
  65. }
  66. }
  67. impl fmt::Display for Host {
  68. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  69. match *self {
  70. Host::Domain(ref domain) => domain.fmt(f),
  71. Host::Ipv4(ref addr) => addr.fmt(f),
  72. Host::Ipv6(ref addr) => write!(f, "[{}]", addr),
  73. }
  74. }
  75. }
  76. fn parse_ipv4number(mut input: &str) -> ParseResult<u32> {
  77. let mut r = 10;
  78. if input.starts_with("0x") || input.starts_with("0X") {
  79. input = &input[2..];
  80. r = 16;
  81. } else if input.len() >= 2 && input.starts_with("0") {
  82. input = &input[1..];
  83. r = 8;
  84. }
  85. if input.is_empty() {
  86. return Ok(0);
  87. }
  88. if input.starts_with("+") {
  89. return Err(ParseError::InvalidIpv4Address)
  90. }
  91. match u32::from_str_radix(&input, r) {
  92. Ok(number) => return Ok(number),
  93. Err(_) => Err(ParseError::InvalidIpv4Address),
  94. }
  95. }
  96. fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
  97. let mut parts: Vec<&str> = input.split('.').collect();
  98. if parts.last() == Some(&"") {
  99. parts.pop();
  100. }
  101. if parts.len() > 4 {
  102. return Ok(None);
  103. }
  104. let mut numbers: Vec<u32> = Vec::new();
  105. for part in parts {
  106. if part == "" {
  107. return Ok(None);
  108. }
  109. if let Ok(n) = parse_ipv4number(part) {
  110. numbers.push(n);
  111. } else {
  112. return Ok(None);
  113. }
  114. }
  115. let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
  116. if ipv4 > u32::max_value() >> (8 * numbers.len() as u32) {
  117. return Err(ParseError::InvalidIpv4Address);
  118. }
  119. if numbers.iter().any(|x| *x > 255) {
  120. return Err(ParseError::InvalidIpv4Address);
  121. }
  122. for (counter, n) in numbers.iter().enumerate() {
  123. ipv4 += n << (8 * (3 - counter as u32))
  124. }
  125. Ok(Some(Ipv4Addr::from(ipv4)))
  126. }
  127. fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
  128. let input = input.as_bytes();
  129. let len = input.len();
  130. let mut is_ip_v4 = false;
  131. let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
  132. let mut piece_pointer = 0;
  133. let mut compress_pointer = None;
  134. let mut i = 0;
  135. if len < 2 {
  136. return Err(ParseError::InvalidIpv6Address)
  137. }
  138. if input[0] == b':' {
  139. if input[1] != b':' {
  140. return Err(ParseError::InvalidIpv6Address)
  141. }
  142. i = 2;
  143. piece_pointer = 1;
  144. compress_pointer = Some(1);
  145. }
  146. while i < len {
  147. if piece_pointer == 8 {
  148. return Err(ParseError::InvalidIpv6Address)
  149. }
  150. if input[i] == b':' {
  151. if compress_pointer.is_some() {
  152. return Err(ParseError::InvalidIpv6Address)
  153. }
  154. i += 1;
  155. piece_pointer += 1;
  156. compress_pointer = Some(piece_pointer);
  157. continue
  158. }
  159. let start = i;
  160. let end = cmp::min(len, start + 4);
  161. let mut value = 0u16;
  162. while i < end {
  163. match from_hex(input[i]) {
  164. Some(digit) => {
  165. value = value * 0x10 + digit as u16;
  166. i += 1;
  167. },
  168. None => break
  169. }
  170. }
  171. if i < len {
  172. match input[i] {
  173. b'.' => {
  174. if i == start {
  175. return Err(ParseError::InvalidIpv6Address)
  176. }
  177. i = start;
  178. is_ip_v4 = true;
  179. },
  180. b':' => {
  181. i += 1;
  182. if i == len {
  183. return Err(ParseError::InvalidIpv6Address)
  184. }
  185. },
  186. _ => return Err(ParseError::InvalidIpv6Address)
  187. }
  188. }
  189. if is_ip_v4 {
  190. break
  191. }
  192. pieces[piece_pointer] = value;
  193. piece_pointer += 1;
  194. }
  195. if is_ip_v4 {
  196. if piece_pointer > 6 {
  197. return Err(ParseError::InvalidIpv6Address)
  198. }
  199. let mut dots_seen = 0;
  200. while i < len {
  201. // FIXME: https://github.com/whatwg/url/commit/1c22aa119c354e0020117e02571cec53f7c01064
  202. let mut value = 0u16;
  203. while i < len {
  204. let digit = match input[i] {
  205. c @ b'0' ... b'9' => c - b'0',
  206. _ => break
  207. };
  208. value = value * 10 + digit as u16;
  209. if value == 0 || value > 255 {
  210. return Err(ParseError::InvalidIpv6Address)
  211. }
  212. }
  213. if dots_seen < 3 && !(i < len && input[i] == b'.') {
  214. return Err(ParseError::InvalidIpv6Address)
  215. }
  216. pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
  217. if dots_seen == 0 || dots_seen == 2 {
  218. piece_pointer += 1;
  219. }
  220. i += 1;
  221. if dots_seen == 3 && i < len {
  222. return Err(ParseError::InvalidIpv6Address)
  223. }
  224. dots_seen += 1;
  225. }
  226. }
  227. match compress_pointer {
  228. Some(compress_pointer) => {
  229. let mut swaps = piece_pointer - compress_pointer;
  230. piece_pointer = 7;
  231. while swaps > 0 {
  232. pieces[piece_pointer] = pieces[compress_pointer + swaps - 1];
  233. pieces[compress_pointer + swaps - 1] = 0;
  234. swaps -= 1;
  235. piece_pointer -= 1;
  236. }
  237. }
  238. _ => if piece_pointer != 8 {
  239. return Err(ParseError::InvalidIpv6Address)
  240. }
  241. }
  242. Ok(Ipv6Addr::new(pieces[0], pieces[1], pieces[2], pieces[3],
  243. pieces[4], pieces[5], pieces[6], pieces[7]))
  244. }