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