Rustfmt net crate

This commit is contained in:
Pyfisch 2018-11-03 15:28:48 +01:00
parent ba1ed11ced
commit 2481ad25f8
30 changed files with 4957 additions and 2870 deletions

View file

@ -35,7 +35,8 @@ use std::thread;
use subresource_integrity::is_response_integrity_valid;
lazy_static! {
static ref X_CONTENT_TYPE_OPTIONS: HeaderName = HeaderName::from_static("x-content-type-options");
static ref X_CONTENT_TYPE_OPTIONS: HeaderName =
HeaderName::from_static("x-content-type-options");
}
const FILE_CHUNK_SIZE: usize = 32768; //32 KB
@ -87,16 +88,16 @@ impl CancellationListener {
pub type DoneChannel = Option<(Sender<Data>, Receiver<Data>)>;
/// [Fetch](https://fetch.spec.whatwg.org#concept-fetch)
pub fn fetch(request: &mut Request,
target: Target,
context: &FetchContext) {
pub fn fetch(request: &mut Request, target: Target, context: &FetchContext) {
fetch_with_cors_cache(request, &mut CorsCache::new(), target, context);
}
pub fn fetch_with_cors_cache(request: &mut Request,
cache: &mut CorsCache,
target: Target,
context: &FetchContext) {
pub fn fetch_with_cors_cache(
request: &mut Request,
cache: &mut CorsCache,
target: Target,
context: &FetchContext,
) {
// Step 1.
if request.window == Window::Client {
// TODO: Set window to request's client object if client is a Window object
@ -132,21 +133,27 @@ pub fn fetch_with_cors_cache(request: &mut Request,
}
/// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch)
pub fn main_fetch(request: &mut Request,
cache: &mut CorsCache,
cors_flag: bool,
recursive_flag: bool,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext)
-> Response {
pub fn main_fetch(
request: &mut Request,
cache: &mut CorsCache,
cors_flag: bool,
recursive_flag: bool,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext,
) -> Response {
// Step 1.
let mut response = None;
// Step 2.
if request.local_urls_only {
if !matches!(request.current_url().scheme(), "about" | "blob" | "data" | "filesystem") {
response = Some(Response::network_error(NetworkError::Internal("Non-local scheme".into())));
if !matches!(
request.current_url().scheme(),
"about" | "blob" | "data" | "filesystem"
) {
response = Some(Response::network_error(NetworkError::Internal(
"Non-local scheme".into(),
)));
}
}
@ -158,7 +165,9 @@ pub fn main_fetch(request: &mut Request,
// Step 5.
if should_be_blocked_due_to_bad_port(&request.current_url()) {
response = Some(Response::network_error(NetworkError::Internal("Request attempted on bad port".into())));
response = Some(Response::network_error(NetworkError::Internal(
"Request attempted on bad port".into(),
)));
}
// TODO: handle blocking as mixed content.
// TODO: handle blocking by content security policy.
@ -167,7 +176,9 @@ pub fn main_fetch(request: &mut Request,
// TODO: handle request's client's referrer policy.
// Step 7.
request.referrer_policy = request.referrer_policy.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));
request.referrer_policy = request
.referrer_policy
.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));
// Step 8.
{
@ -182,11 +193,13 @@ pub fn main_fetch(request: &mut Request,
Referrer::ReferrerUrl(url) => {
request.headers.remove(header::REFERER);
let current_url = request.current_url().clone();
determine_request_referrer(&mut request.headers,
request.referrer_policy.unwrap(),
url,
current_url)
}
determine_request_referrer(
&mut request.headers,
request.referrer_policy.unwrap(),
url,
current_url,
)
},
};
if let Some(referrer_url) = referrer_url {
request.referrer = Referrer::ReferrerUrl(referrer_url);
@ -197,8 +210,12 @@ pub fn main_fetch(request: &mut Request,
// TODO: handle FTP URLs.
// Step 10.
context.state.hsts_list.read().unwrap().switch_known_hsts_host_domain_url_to_https(
request.current_url_mut());
context
.state
.hsts_list
.read()
.unwrap()
.switch_known_hsts_host_domain_url_to_https(request.current_url_mut());
// Step 11.
// Not applicable: see fetch_async.
@ -218,47 +235,48 @@ pub fn main_fetch(request: &mut Request,
// and about: schemes, but CSS tests will break on loading Ahem
// since we load them through a file: URL.
current_url.scheme() == "about" ||
request.mode == RequestMode::Navigate {
request.mode == RequestMode::Navigate
{
// Substep 1.
request.response_tainting = ResponseTainting::Basic;
// Substep 2.
scheme_fetch(request, cache, target, done_chan, context)
} else if request.mode == RequestMode::SameOrigin {
Response::network_error(NetworkError::Internal("Cross-origin response".into()))
} else if request.mode == RequestMode::NoCors {
// Substep 1.
request.response_tainting = ResponseTainting::Opaque;
// Substep 2.
scheme_fetch(request, cache, target, done_chan, context)
} else if !matches!(current_url.scheme(), "http" | "https") {
Response::network_error(NetworkError::Internal("Non-http scheme".into()))
} else if request.use_cors_preflight ||
(request.unsafe_request &&
(!is_cors_safelisted_method(&request.method) ||
request.headers.iter().any(|(name, value)| !is_cors_safelisted_request_header(&name, &value)))) {
request.headers.iter().any(|(name, value)| {
!is_cors_safelisted_request_header(&name, &value)
}))) {
// Substep 1.
request.response_tainting = ResponseTainting::CorsTainting;
// Substep 2.
let response = http_fetch(request, cache, true, true, false,
target, done_chan, context);
let response = http_fetch(
request, cache, true, true, false, target, done_chan, context,
);
// Substep 3.
if response.is_network_error() {
// TODO clear cache entries using request
}
// Substep 4.
response
} else {
// Substep 1.
request.response_tainting = ResponseTainting::CorsTainting;
// Substep 2.
http_fetch(request, cache, true, false, false, target, done_chan, context)
http_fetch(
request, cache, true, false, false, target, done_chan, context,
)
}
});
@ -272,19 +290,25 @@ pub fn main_fetch(request: &mut Request,
// Substep 1.
if request.response_tainting == ResponseTainting::CorsTainting {
// Subsubstep 1.
let header_names: Option<Vec<HeaderName>> = response.headers.typed_get::<AccessControlExposeHeaders>()
let header_names: Option<Vec<HeaderName>> = response
.headers
.typed_get::<AccessControlExposeHeaders>()
.map(|v| v.iter().collect());
match header_names {
// Subsubstep 2.
Some(ref list) if request.credentials_mode != CredentialsMode::Include => {
if list.len() == 1 && list[0] == "*" {
response.cors_exposed_header_name_list =
response.headers.iter().map(|(name, _)| name.as_str().to_owned()).collect();
response.cors_exposed_header_name_list = response
.headers
.iter()
.map(|(name, _)| name.as_str().to_owned())
.collect();
}
},
// Subsubstep 3.
Some(list) => {
response.cors_exposed_header_name_list = list.iter().map(|h| h.as_str().to_owned()).collect();
response.cors_exposed_header_name_list =
list.iter().map(|h| h.as_str().to_owned()).collect();
},
_ => (),
}
@ -304,13 +328,16 @@ pub fn main_fetch(request: &mut Request,
let internal_error = {
// Tests for steps 17 and 18, before step 15 for borrowing concerns.
let response_is_network_error = response.is_network_error();
let should_replace_with_nosniff_error =
!response_is_network_error && should_be_blocked_due_to_nosniff(request.destination, &response.headers);
let should_replace_with_mime_type_error =
!response_is_network_error && should_be_blocked_due_to_mime_type(request.destination, &response.headers);
let should_replace_with_nosniff_error = !response_is_network_error &&
should_be_blocked_due_to_nosniff(request.destination, &response.headers);
let should_replace_with_mime_type_error = !response_is_network_error &&
should_be_blocked_due_to_mime_type(request.destination, &response.headers);
// Step 15.
let mut network_error_response = response.get_network_error().cloned().map(Response::network_error);
let mut network_error_response = response
.get_network_error()
.cloned()
.map(Response::network_error);
let internal_response = if let Some(error_response) = network_error_response.as_mut() {
error_response
} else {
@ -326,27 +353,30 @@ pub fn main_fetch(request: &mut Request,
// TODO: handle blocking as mixed content.
// TODO: handle blocking by content security policy.
let blocked_error_response;
let internal_response =
if should_replace_with_nosniff_error {
// Defer rebinding result
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
&blocked_error_response
} else if should_replace_with_mime_type_error {
// Defer rebinding result
blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by mime type".into()));
&blocked_error_response
} else {
internal_response
};
let internal_response = if should_replace_with_nosniff_error {
// Defer rebinding result
blocked_error_response =
Response::network_error(NetworkError::Internal("Blocked by nosniff".into()));
&blocked_error_response
} else if should_replace_with_mime_type_error {
// Defer rebinding result
blocked_error_response =
Response::network_error(NetworkError::Internal("Blocked by mime type".into()));
&blocked_error_response
} else {
internal_response
};
// Step 18.
// We check `internal_response` since we did not mutate `response`
// in the previous step.
let not_network_error = !response_is_network_error && !internal_response.is_network_error();
if not_network_error && (is_null_body_status(&internal_response.status) ||
match request.method {
Method::HEAD | Method::CONNECT => true,
_ => false }) {
if not_network_error &&
(is_null_body_status(&internal_response.status) ||
match request.method {
Method::HEAD | Method::CONNECT => true,
_ => false,
}) {
// when Fetch is used only asynchronously, we will need to make sure
// that nothing tries to write to the body at this point
let mut body = internal_response.body.lock().unwrap();
@ -373,8 +403,11 @@ pub fn main_fetch(request: &mut Request,
// Step 19.2.
let ref integrity_metadata = &request.integrity_metadata;
if response.termination_reason.is_none() &&
!is_response_integrity_valid(integrity_metadata, &response) {
Response::network_error(NetworkError::Internal("Subresource integrity validation failed".into()))
!is_response_integrity_valid(integrity_metadata, &response)
{
Response::network_error(NetworkError::Internal(
"Subresource integrity validation failed".into(),
))
} else {
response
}
@ -410,7 +443,7 @@ pub fn main_fetch(request: &mut Request,
// Step 23.
if !response_loaded {
wait_for_response(&mut response, target, done_chan);
wait_for_response(&mut response, target, done_chan);
}
// Step 24.
@ -430,8 +463,11 @@ pub fn main_fetch(request: &mut Request,
fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut DoneChannel) {
if let Some(ref ch) = *done_chan {
loop {
match ch.1.recv()
.expect("fetch worker should always send Done before terminating") {
match ch
.1
.recv()
.expect("fetch worker should always send Done before terminating")
{
Data::Payload(vec) => {
target.process_response_chunk(vec);
},
@ -439,7 +475,7 @@ fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut Do
Data::Cancelled => {
response.aborted.store(true, Ordering::Relaxed);
break;
}
},
}
}
} else {
@ -456,36 +492,39 @@ fn wait_for_response(response: &mut Response, target: Target, done_chan: &mut Do
}
/// [Scheme fetch](https://fetch.spec.whatwg.org#scheme-fetch)
fn scheme_fetch(request: &mut Request,
cache: &mut CorsCache,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext)
-> Response {
fn scheme_fetch(
request: &mut Request,
cache: &mut CorsCache,
target: Target,
done_chan: &mut DoneChannel,
context: &FetchContext,
) -> Response {
let url = request.current_url();
match url.scheme() {
"about" if url.path() == "blank" => {
let mut response = Response::new(url);
response.headers.typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
response
.headers
.typed_insert(ContentType::from(mime::TEXT_HTML_UTF_8));
*response.body.lock().unwrap() = ResponseBody::Done(vec![]);
response
},
"http" | "https" => {
http_fetch(request, cache, false, false, false, target, done_chan, context)
},
"http" | "https" => http_fetch(
request, cache, false, false, false, target, done_chan, context,
),
"data" => {
match decode(&url) {
Ok((mime, bytes)) => {
let mut response = Response::new(url);
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
response.headers.typed_insert(ContentType::from(mime));
response
},
Err(_) => Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
}
"data" => match decode(&url) {
Ok((mime, bytes)) => {
let mut response = Response::new(url);
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
response.headers.typed_insert(ContentType::from(mime));
response
},
Err(_) => {
Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
},
},
"file" => {
@ -507,9 +546,17 @@ fn scheme_fetch(request: &mut Request,
let cancellation_listener = context.cancellation_listener.clone();
let (start, end) = if let Some(ref range) = request.headers.typed_get::<Range>() {
match range.iter().collect::<Vec<(Bound<u64>, Bound<u64>)>>().first() {
Some(&(Bound::Included(start), Bound::Unbounded)) => (start, None),
let (start, end) = if let Some(ref range) =
request.headers.typed_get::<Range>()
{
match range
.iter()
.collect::<Vec<(Bound<u64>, Bound<u64>)>>()
.first()
{
Some(&(Bound::Included(start), Bound::Unbounded)) => {
(start, None)
},
Some(&(Bound::Included(start), Bound::Included(end))) => {
// `end` should be less or equal to `start`.
(start, Some(u64::max(start, end)))
@ -517,75 +564,97 @@ fn scheme_fetch(request: &mut Request,
Some(&(Bound::Unbounded, Bound::Included(offset))) => {
if let Ok(metadata) = file.metadata() {
// `offset` cannot be bigger than the file size.
(metadata.len() - u64::min(metadata.len(), offset), None)
(
metadata.len() -
u64::min(metadata.len(), offset),
None,
)
} else {
(0, None)
}
},
_ => (0, None)
_ => (0, None),
}
} else {
(0, None)
};
thread::Builder::new().name("fetch file worker thread".to_string()).spawn(move || {
let mut reader = BufReader::with_capacity(FILE_CHUNK_SIZE, file);
if reader.seek(SeekFrom::Start(start)).is_err() {
warn!("Fetch - could not seek to {:?}", start);
}
thread::Builder::new()
.name("fetch file worker thread".to_string())
.spawn(move || {
let mut reader =
BufReader::with_capacity(FILE_CHUNK_SIZE, file);
if reader.seek(SeekFrom::Start(start)).is_err() {
warn!("Fetch - could not seek to {:?}", start);
}
loop {
if cancellation_listener.lock().unwrap().cancelled() {
*res_body.lock().unwrap() = ResponseBody::Done(vec![]);
let _ = done_sender.send(Data::Cancelled);
return;
}
let length = {
let mut buffer = reader.fill_buf().unwrap().to_vec();
let mut buffer_len = buffer.len();
if let ResponseBody::Receiving(ref mut body) = *res_body.lock().unwrap() {
let offset = usize::min({
if let Some(end) = end {
let remaining_bytes =
end as usize - start as usize - body.len();
if remaining_bytes <= FILE_CHUNK_SIZE {
// This is the last chunk so we set buffer len to 0 to break
// the reading loop.
buffer_len = 0;
remaining_bytes
} else {
FILE_CHUNK_SIZE
}
} else {
FILE_CHUNK_SIZE
}
}, buffer.len());
body.extend_from_slice(&buffer[0..offset]);
let _ = done_sender.send(Data::Payload(buffer));
loop {
if cancellation_listener.lock().unwrap().cancelled() {
*res_body.lock().unwrap() =
ResponseBody::Done(vec![]);
let _ = done_sender.send(Data::Cancelled);
return;
}
buffer_len
};
if length == 0 {
let mut body = res_body.lock().unwrap();
let completed_body = match *body {
ResponseBody::Receiving(ref mut body) => {
mem::replace(body, vec![])
},
_ => vec![],
let length = {
let mut buffer =
reader.fill_buf().unwrap().to_vec();
let mut buffer_len = buffer.len();
if let ResponseBody::Receiving(ref mut body) =
*res_body.lock().unwrap()
{
let offset = usize::min(
{
if let Some(end) = end {
let remaining_bytes = end as usize -
start as usize -
body.len();
if remaining_bytes <=
FILE_CHUNK_SIZE
{
// This is the last chunk so we set buffer len to 0 to break
// the reading loop.
buffer_len = 0;
remaining_bytes
} else {
FILE_CHUNK_SIZE
}
} else {
FILE_CHUNK_SIZE
}
},
buffer.len(),
);
body.extend_from_slice(&buffer[0..offset]);
let _ = done_sender.send(Data::Payload(buffer));
}
buffer_len
};
*body = ResponseBody::Done(completed_body);
let _ = done_sender.send(Data::Done);
break;
if length == 0 {
let mut body = res_body.lock().unwrap();
let completed_body = match *body {
ResponseBody::Receiving(ref mut body) => {
mem::replace(body, vec![])
},
_ => vec![],
};
*body = ResponseBody::Done(completed_body);
let _ = done_sender.send(Data::Done);
break;
}
reader.consume(length);
}
reader.consume(length);
}
}).expect("Failed to create fetch file worker thread");
})
.expect("Failed to create fetch file worker thread");
response
},
_ => Response::network_error(NetworkError::Internal("Opening file failed".into())),
_ => Response::network_error(NetworkError::Internal(
"Opening file failed".into(),
)),
}
},
_ => Response::network_error(NetworkError::Internal("Constructing file path failed".into()))
_ => Response::network_error(NetworkError::Internal(
"Constructing file path failed".into(),
)),
}
} else {
Response::network_error(NetworkError::Internal("Unexpected method for file".into()))
@ -596,7 +665,9 @@ fn scheme_fetch(request: &mut Request,
println!("Loading blob {}", url.as_str());
// Step 2.
if request.method != Method::GET {
return Response::network_error(NetworkError::Internal("Unexpected method for blob".into()));
return Response::network_error(NetworkError::Internal(
"Unexpected method for blob".into(),
));
}
match load_blob_sync(url.clone(), context.filemanager.clone()) {
@ -618,7 +689,7 @@ fn scheme_fetch(request: &mut Request,
Response::network_error(NetworkError::Internal("Unexpected scheme".into()))
},
_ => Response::network_error(NetworkError::Internal("Unexpected scheme".into()))
_ => Response::network_error(NetworkError::Internal("Unexpected scheme".into())),
}
}
@ -627,13 +698,15 @@ pub fn is_cors_safelisted_request_header(name: &HeaderName, value: &HeaderValue)
if name == header::CONTENT_TYPE {
if let Some(m) = value.to_str().ok().and_then(|s| s.parse::<Mime>().ok()) {
m.type_() == mime::TEXT && m.subtype() == mime::PLAIN ||
m.type_() == mime::APPLICATION && m.subtype() == mime::WWW_FORM_URLENCODED ||
m.type_() == mime::MULTIPART && m.subtype() == mime::FORM_DATA
m.type_() == mime::APPLICATION && m.subtype() == mime::WWW_FORM_URLENCODED ||
m.type_() == mime::MULTIPART && m.subtype() == mime::FORM_DATA
} else {
false
}
} else {
name == header::ACCEPT || name == header::ACCEPT_LANGUAGE || name == header::CONTENT_LANGUAGE
name == header::ACCEPT ||
name == header::ACCEPT_LANGUAGE ||
name == header::CONTENT_LANGUAGE
}
}
@ -641,28 +714,35 @@ pub fn is_cors_safelisted_request_header(name: &HeaderName, value: &HeaderValue)
pub fn is_cors_safelisted_method(m: &Method) -> bool {
match *m {
Method::GET | Method::HEAD | Method::POST => true,
_ => false
_ => false,
}
}
fn is_null_body_status(status: &Option<(StatusCode, String)>) -> bool {
match *status {
Some((status, _)) => match status {
StatusCode::SWITCHING_PROTOCOLS | StatusCode::NO_CONTENT |
StatusCode::RESET_CONTENT | StatusCode::NOT_MODIFIED => true,
_ => false
StatusCode::SWITCHING_PROTOCOLS |
StatusCode::NO_CONTENT |
StatusCode::RESET_CONTENT |
StatusCode::NOT_MODIFIED => true,
_ => false,
},
_ => false
_ => false,
}
}
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?>
pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_headers: &HeaderMap) -> bool {
pub fn should_be_blocked_due_to_nosniff(
destination: Destination,
response_headers: &HeaderMap,
) -> bool {
// Steps 1-3.
// TODO(eijebong): Replace this once typed headers allow custom ones...
if response_headers.get("x-content-type-options")
.map_or(true, |val| val.to_str().unwrap_or("").to_lowercase() != "nosniff")
{
if response_headers
.get("x-content-type-options")
.map_or(true, |val| {
val.to_str().unwrap_or("").to_lowercase() != "nosniff"
}) {
return false;
}
@ -692,30 +772,34 @@ pub fn should_be_blocked_due_to_nosniff(destination: Destination, response_heade
"text/x-javascript".parse().unwrap(),
];
javascript_mime_types.iter()
javascript_mime_types
.iter()
.any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
}
match content_type_header {
// Step 6
Some(ref ct) if destination.is_script_like()
=> !is_javascript_mime_type(&ct.clone().into()),
Some(ref ct) if destination.is_script_like() => {
!is_javascript_mime_type(&ct.clone().into())
},
// Step 7
Some(ref ct) if destination == Destination::Style
=> {
let m: mime::Mime = ct.clone().into();
m.type_() != mime::TEXT && m.subtype() != mime::CSS
},
Some(ref ct) if destination == Destination::Style => {
let m: mime::Mime = ct.clone().into();
m.type_() != mime::TEXT && m.subtype() != mime::CSS
},
None if destination == Destination::Style || destination.is_script_like() => true,
// Step 8
_ => false
_ => false,
}
}
/// <https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-mime-type?>
fn should_be_blocked_due_to_mime_type(destination: Destination, response_headers: &HeaderMap) -> bool {
fn should_be_blocked_due_to_mime_type(
destination: Destination,
response_headers: &HeaderMap,
) -> bool {
// Step 1
let mime_type: mime::Mime = match response_headers.typed_get::<ContentType>() {
Some(header) => header.into(),
@ -725,12 +809,10 @@ fn should_be_blocked_due_to_mime_type(destination: Destination, response_headers
// Step 2-3
destination.is_script_like() &&
match mime_type.type_() {
mime::AUDIO |
mime::VIDEO |
mime::IMAGE => true,
mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
mime::TEXT if mime_type.subtype() == mime::CSV => true,
// Step 4
_ => false
_ => false,
}
}
@ -745,14 +827,17 @@ pub fn should_be_blocked_due_to_bad_port(url: &ServoUrl) -> bool {
// If there is no explicit port, this means the default one is used for
// the given scheme, and thus this means the request should not be blocked
// due to a bad port.
let port = if let Some(port) = url.port() { port } else { return false };
let port = if let Some(port) = url.port() {
port
} else {
return false;
};
// Step 4.
if scheme == "ftp" && (port == 20 || port == 21) {
return false;
}
// Step 5.
if is_network_scheme(scheme) && is_bad_port(port) {
return true;
@ -770,12 +855,10 @@ fn is_network_scheme(scheme: &str) -> bool {
/// <https://fetch.spec.whatwg.org/#bad-port>
fn is_bad_port(port: u16) -> bool {
static BAD_PORTS: [u16; 64] = [
1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42,
43, 53, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111,
113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512,
513, 514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601,
636, 993, 995, 2049, 3659, 4045, 6000, 6665, 6666, 6667,
6668, 6669
1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 77, 79, 87, 95, 101, 102,
103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 139, 143, 179, 389, 465, 512, 513,
514, 515, 526, 530, 531, 532, 540, 556, 563, 587, 601, 636, 993, 995, 2049, 3659, 4045,
6000, 6665, 6666, 6667, 6668, 6669,
];
BAD_PORTS.binary_search(&port).is_ok()