Restore server name validation

This commit is contained in:
Jonas Platte 2020-04-17 12:06:40 +02:00
parent b48235f496
commit e8f7bd9f6d
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
8 changed files with 129 additions and 26 deletions

View File

@ -16,6 +16,8 @@ Breaking changes:
* Note that hashes are generally only guaranteed consistent in the lifetime of the program
though, so do not persist them!
* The `hostname` methods have been updated to return string slices instead of `&url::Host`
* `Error::InvalidHost` has been renamed to `Error::InvalidServerName`, because it also covers errors
in the port, not just the host part section of the server name
Improvements:

View File

@ -11,8 +11,8 @@ pub enum Error {
InvalidCharacters,
/// The localpart of the ID string is not valid (because it is empty).
InvalidLocalPart,
/// The domain part of the the ID string is not a valid IP address or DNS name.
InvalidHost,
/// The server name part of the the ID string is not a valid server name.
InvalidServerName,
/// The ID exceeds 255 bytes (or 32 codepoints for a room version ID.)
MaximumLengthExceeded,
/// The ID is less than 4 characters (or is an empty room version ID.)
@ -28,7 +28,7 @@ impl Display for Error {
let message = match self {
Error::InvalidCharacters => "localpart contains invalid characters",
Error::InvalidLocalPart => "localpart is empty",
Error::InvalidHost => "server name is not a valid IP address or domain name",
Error::InvalidServerName => "server name is not a valid IP address or domain name",
Error::MaximumLengthExceeded => "ID exceeds 255 bytes",
Error::MinimumLengthNotSatisfied => "ID must be at least 4 characters",
Error::MissingDelimiter => "colon is required between localpart and server name",

View File

@ -162,10 +162,10 @@ mod tests {
assert_eq!(id_str.len(), 31);
}
/*#[test]
#[test]
fn generate_random_invalid_event_id() {
assert!(EventId::new("").is_err());
}*/
}
#[test]
fn serialize_valid_original_event_id() {
@ -275,11 +275,11 @@ mod tests {
);
}
/*#[test]
#[test]
fn invalid_event_id_host() {
assert_eq!(
EventId::try_from("$39hvsi03hlne:/").unwrap_err(),
Error::InvalidHost
Error::InvalidServerName
);
}
@ -287,7 +287,7 @@ mod tests {
fn invalid_event_id_port() {
assert_eq!(
EventId::try_from("$39hvsi03hlne:example.com:notaport").unwrap_err(),
Error::InvalidHost
Error::InvalidServerName
);
}*/
}
}

View File

@ -23,7 +23,8 @@ use serde::de::{self, Deserialize as _, Deserializer, Unexpected};
pub use crate::device_id::DeviceId;
pub use crate::{
error::Error, event_id::EventId, room_alias_id::RoomAliasId, room_id::RoomId,
room_id_or_room_alias_id::RoomIdOrAliasId, room_version_id::RoomVersionId, user_id::UserId,
room_id_or_room_alias_id::RoomIdOrAliasId, room_version_id::RoomVersionId,
server_name::is_valid_server_name, user_id::UserId,
};
#[macro_use]
@ -38,6 +39,7 @@ mod room_alias_id;
mod room_id;
mod room_id_or_room_alias_id;
mod room_version_id;
mod server_name;
mod user_id;
/// All identifiers must be 255 bytes or less.
@ -79,8 +81,8 @@ fn parse_id(id: &str, valid_sigils: &[char]) -> Result<NonZeroU8, Error> {
validate_id(id, valid_sigils)?;
let colon_idx = id.find(':').ok_or(Error::MissingDelimiter)?;
if colon_idx == id.len() - 1 {
return Err(Error::InvalidHost);
if !is_valid_server_name(&id[colon_idx + 1..]) {
return Err(Error::InvalidServerName);
}
match NonZeroU8::new(colon_idx as u8) {

View File

@ -144,11 +144,11 @@ mod tests {
);
}
/*#[test]
#[test]
fn invalid_room_alias_id_host() {
assert_eq!(
RoomAliasId::try_from("#ruma:/").unwrap_err(),
Error::InvalidHost
Error::InvalidServerName
);
}
@ -156,7 +156,7 @@ mod tests {
fn invalid_room_alias_id_port() {
assert_eq!(
RoomAliasId::try_from("#ruma:example.com:notaport").unwrap_err(),
Error::InvalidHost
Error::InvalidServerName
);
}*/
}
}

View File

@ -101,10 +101,10 @@ mod tests {
assert_eq!(id_str.len(), 31);
}
/*#[test]
#[test]
fn generate_random_invalid_room_id() {
assert!(RoomId::new("").is_err());
}*/
}
#[test]
fn serialize_valid_room_id() {
@ -162,11 +162,11 @@ mod tests {
);
}
/*#[test]
#[test]
fn invalid_room_id_host() {
assert_eq!(
RoomId::try_from("!29fhd83h92h0:/").unwrap_err(),
Error::InvalidHost
Error::InvalidServerName
);
}
@ -174,7 +174,7 @@ mod tests {
fn invalid_room_id_port() {
assert_eq!(
RoomId::try_from("!29fhd83h92h0:example.com:notaport").unwrap_err(),
Error::InvalidHost
Error::InvalidServerName
);
}*/
}
}

96
src/server_name.rs Normal file
View File

@ -0,0 +1,96 @@
/// Check whether a given string is a valid server name according to [the specification][].
///
/// [the specification]: https://matrix.org/docs/spec/appendices#server-name
pub fn is_valid_server_name(name: &str) -> bool {
use std::net::Ipv6Addr;
let end_of_host = if name.starts_with('[') {
let end_of_ipv6 = match name.find(']') {
Some(idx) => idx,
None => return false,
};
if name[1..end_of_ipv6].parse::<Ipv6Addr>().is_err() {
return false;
}
end_of_ipv6 + 1
} else {
let end_of_host = name.find(':').unwrap_or_else(|| name.len());
if name[..end_of_host]
.bytes()
.any(|byte| !(byte.is_ascii_alphanumeric() || byte == b'-' || byte == b'.'))
{
return false;
}
end_of_host
};
if name.len() == end_of_host {
true
} else if name.as_bytes()[end_of_host] != b':' {
// hostname is followed by something other than ":port"
false
} else {
// are the remaining characters after ':' a valid port?
name[end_of_host + 1..].parse::<u16>().is_ok()
}
}
#[cfg(test)]
mod tests {
use super::is_valid_server_name;
#[test]
fn ipv4_host() {
assert!(is_valid_server_name("127.0.0.1"));
}
#[test]
fn ipv4_host_and_port() {
assert!(is_valid_server_name("1.1.1.1:12000"));
}
#[test]
fn ipv6() {
assert!(is_valid_server_name("[::1]"));
}
#[test]
fn ipv6_with_port() {
assert!(is_valid_server_name("[1234:5678::abcd]:5678"));
}
#[test]
fn dns_name() {
assert!(is_valid_server_name("example.com"));
}
#[test]
fn dns_name_with_port() {
assert!(is_valid_server_name("ruma.io:8080"));
}
#[test]
fn invalid_ipv6() {
assert!(!is_valid_server_name("[test::1]"));
}
#[test]
fn ipv4_with_invalid_port() {
assert!(!is_valid_server_name("127.0.0.1:"));
}
#[test]
fn ipv6_with_invalid_port() {
assert!(!is_valid_server_name("[fe80::1]:100000"));
assert!(!is_valid_server_name("[fe80::1]!"));
}
#[test]
fn dns_name_with_invalid_port() {
assert!(!is_valid_server_name("matrix.org:hello"));
}
}

View File

@ -209,16 +209,19 @@ mod tests {
);
}
/*#[test]
#[test]
fn invalid_user_id_host() {
assert_eq!(UserId::try_from("@carl:/").unwrap_err(), Error::InvalidHost);
assert_eq!(
UserId::try_from("@carl:/").unwrap_err(),
Error::InvalidServerName
);
}
#[test]
fn invalid_user_id_port() {
assert_eq!(
UserId::try_from("@carl:example.com:notaport").unwrap_err(),
Error::InvalidHost
Error::InvalidServerName
);
}*/
}
}