diff --git c/src/service/resolver/actual.rs i/src/service/resolver/actual.rs
index 9cd1aec15..495d83172 100644
--- c/src/service/resolver/actual.rs
+++ i/src/service/resolver/actual.rs
@@ -1,9 +1,5 @@
use std::fmt::Debug;
-use super::{
- cache::{CachedDest, CachedOverride, MAX_IPS},
- fed::{FedDest, PortString, add_port_to_hostname, ensure_host_has_port, get_ip_with_port},
-};
use conduwuit::{Err, Result, debug, debug_info, err, error, trace};
use futures::{FutureExt, TryFutureExt};
use hickory_resolver::{
@@ -15,9 +11,11 @@
use super::{
cache::{CachedDest, CachedOverride, MAX_IPS},
- fed::{FedDest, PortString, add_port_to_hostname, get_ip_with_port},
+ fed::{FedDest, PortString, add_port_to_hostname, ensure_host_has_port, get_ip_with_port},
};
+const DEFAULT_PORT: u16 = 8448;
+
#[derive(Clone, Debug)]
pub(crate) struct ActualDest {
pub(crate) dest: FedDest,
@@ -26,9 +24,7 @@ pub(crate) struct ActualDest {
impl ActualDest {
#[inline]
- pub(crate) fn string(&self) -> String {
- self.dest.https_string()
- }
+ pub(crate) fn string(&self) -> String { self.dest.https_string() }
}
impl super::Service {
@@ -75,7 +71,8 @@ pub async fn resolve_actual_dest(
// Ensure dest is a valid connection endpoint
self.validate_dest(dest)?;
- // Clippy believes this can be a clone, however we are actually converting ServerName to String
+ // Clippy believes this can be a clone, however we are actually converting
+ // ServerName to String
#[allow(clippy::implicit_clone)]
let mut host = dest.to_string().to_owned();
let actual_dest = self.resolve_server_name(dest, cache, &mut host).await?;
@@ -103,8 +100,8 @@ async fn resolve_server_name(
cache: bool,
host: &mut String,
) -> Result<FedDest> {
- // 1. If `dest` is an IP, use it directly. If a port is provided as well (IP:port socket pair)
- // use that, otherwise default to port 8448
+ // 1. If `dest` is an IP, use it directly. If a port is provided as well
+ // (IP:port socket pair) use that, otherwise default to port 8448
if let Some(fed_dest) = get_ip_with_port(dest.as_str()) {
debug!("1: IP literal with provided or default port");
return Ok(fed_dest);
@@ -117,8 +114,8 @@ async fn resolve_server_name(
.await?;
}
- // Pre-resolve IP? Unsure what overrides exactly do, system is due to be removed either way
- // https://matrix.to/#/!da26JtAjE6APGLnX8ncWsvc-skF2KQZ9Nw_MbNpYD2k/%24_hq6JP0JXANbMTMPdV64iZbgbsZdhy92M5ndDYGy6No
+ // Pre-resolve IP? Unsure what overrides exactly do, system is due to be removed
+ // either way https://matrix.to/#/!da26JtAjE6APGLnX8ncWsvc-skF2KQZ9Nw_MbNpYD2k/%24_hq6JP0JXANbMTMPdV64iZbgbsZdhy92M5ndDYGy6No
self.conditional_query_and_cache(dest.as_str(), DEFAULT_PORT, true)
.await?;
@@ -126,7 +123,8 @@ async fn resolve_server_name(
self.services.server.check_running()?;
// 3. If `dest` is a hostname with no port, send GET to `https://<dest>/.well-known/matrix/server`.
- // If invalid JSON (throws error), skip to step 4. Otherwise, parse `delegated` as `<hostname>[:<port>]` and...
+ // If invalid JSON (throws error), skip to step 4. Otherwise, parse `delegated`
+ // as `<hostname>[:<port>]` and...
if let Some(delegated) = self.request_well_known(dest.as_str()).await? {
// delegated=matrix-federation.matrix.org:443 // host=matrix.org
self.resolve_3_well_known(host, cache, delegated).await?;
@@ -137,11 +135,13 @@ async fn resolve_server_name(
self.resolve_4_srv_lookup(host, cache, overrider).await?;
}
- // 5. if .well-known errored and no SRV exists, resolve IP and connect on default port (8448)
+ // 5. if .well-known errored and no SRV exists, resolve IP and connect on
+ // default port (8448)
self.resolve_5_direct(dest, cache).await
}
- /// Parse a host:port socket pair into separate parts, and resolve the hostname into an IP address
+ /// Parse a host:port socket pair into separate parts, and resolve the
+ /// hostname into an IP address
async fn resolve_2_host_port(
&self,
dest: &ServerName,
@@ -180,14 +180,16 @@ async fn resolve_3_well_known(
return Ok(host_and_port);
}
- // 3.2 - If <delegated> is not an IP and a port is present, lookup IP for hostname and connect
+ // 3.2 - If <delegated> is not an IP and a port is present, lookup IP for
+ // hostname and connect
if let Some(pos) = &delegated.find(':') {
self.resolve_3_2_hostname_port(cache, &delegated, *pos)
.await?;
}
- // 3.3 - If <delegated> is not an IP and there is no port, lookup SRV `_matrix._tcp.<delegated>`
- // (which may provide a new hostname + port to use, see steps 3.1 and 3.2)
+ // 3.3 - If <delegated> is not an IP and there is no port, lookup SRV
+ // `_matrix._tcp.<delegated>` (which may provide a new hostname + port to use,
+ // see steps 3.1 and 3.2)
trace!("Delegated hostname has no port, querying SRV");
if let Some(overrider) = self.query_srv_record(&delegated).await? {
self.resolve_3_3_use_srv(cache, &delegated, overrider)
@@ -342,17 +344,14 @@ async fn query_and_cache_override(
match self.resolver.resolver.lookup_ip(hostname.to_owned()).await {
| Err(e) => Self::handle_resolve_error(&e, hostname),
| Ok(override_ip) => {
- self.cache.set_override(
- untername,
- &CachedOverride {
- ips: override_ip.iter().take(MAX_IPS).collect(),
- port,
- expire: CachedOverride::default_expire(),
- overriding: (hostname != untername)
- .then_some(hostname.into())
- .inspect(|_| debug_info!("{untername:?} overridden by {hostname:?}")),
- },
- );
+ self.cache.set_override(untername, &CachedOverride {
+ ips: override_ip.into_iter().take(MAX_IPS).collect(),
+ port,
+ expire: CachedOverride::default_expire(),
+ overriding: (hostname != untername)
+ .then_some(hostname.into())
+ .inspect(|_| debug_info!("{untername:?} overridden by {hostname:?}")),
+ });
Ok(())
},
@@ -415,7 +414,8 @@ fn handle_resolve_error(err: &NetError, host: &'_ str) -> Result<()> {
}
}
- /// Ensure `dest` is a valid destination (valid ip if it is an IP), and not ourselves (unless in config)
+ /// Ensure `dest` is a valid destination (valid ip if it is an IP), and not
+ /// ourselves (unless in config)
fn validate_dest(&self, dest: &ServerName) -> Result<()> {
if dest == self.services.server.name && !self.services.server.config.federation_loopback {
return Err!("Won't send federation request to ourselves");
diff --git c/src/service/resolver/fed.rs i/src/service/resolver/fed.rs
index b43f62eed..83601a98a 100644
--- c/src/service/resolver/fed.rs
+++ i/src/service/resolver/fed.rs
@@ -9,8 +9,8 @@
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub enum FedDest {
- Literal(SocketAddr),
- Named(String, PortString),
+ Literal(SocketAddr), // "ip:port"
+ Named(String, PortString), // ("hostname", ":port")
}
/// numeric or service-name
@@ -18,6 +18,9 @@ pub enum FedDest {
const DEFAULT_PORT: &str = ":8448";
+/// Attempt to parse `dest_str` as either an IP:port socket pair or as a plain
+/// IP (adding the default port), returning `None` if dest_str is neither a
+/// socket pair nor a plain IP.
pub(crate) fn get_ip_with_port(dest_str: &str) -> Option<FedDest> {
if let Ok(dest) = dest_str.parse::<SocketAddr>() {
Some(FedDest::Literal(dest))
@@ -28,6 +31,8 @@ pub(crate) fn get_ip_with_port(dest_str: &str) -> Option<FedDest> {
}
}
+/// Convert a `dest` string with or without port into a FedDest with either
+/// the provided port (if host:port format) or the default port (8448)
pub(crate) fn add_port_to_hostname(dest: &str) -> FedDest {
let (host, port) = match dest.find(':') {
| None => (dest, DEFAULT_PORT),
@@ -42,8 +47,8 @@ pub(crate) fn add_port_to_hostname(dest: &str) -> FedDest {
/// Ensure `host` always has a port
///
-/// `get_ip_with_port` returns `None` if `host` isn't an IP:port string or plain IP,
-/// in which case `add_port_to_hostname` adds it instead
+/// `get_ip_with_port` returns `None` if `host` isn't an IP:port string or plain
+/// IP, in which case `add_port_to_hostname` adds it instead
#[inline]
pub(crate) fn ensure_host_has_port(host: &str) -> FedDest {
get_ip_with_port(host).unwrap_or_else(|| add_port_to_hostname(host))
continuwuity
A community-driven Matrix homeserver in Rust
continuwuity is a Matrix homeserver written in Rust. It's the official community continuation of the conduwuit homeserver.
Why does this exist?
The original conduwuit project has been archived and is no longer maintained. Rather than letting this Rust-based Matrix homeserver disappear, a group of community contributors have forked the project to continue its development, fix outstanding issues, and add new features.
We aim to provide a stable, well-maintained alternative for current conduwuit users and welcome newcomers seeking a lightweight, efficient Matrix homeserver.
Who are we?
We are a group of Matrix enthusiasts, developers and system administrators who have used conduwuit and believe in its potential. Our team includes both previous contributors to the original project and new developers who want to help maintain and improve this important piece of Matrix infrastructure.
We operate as an open community project, welcoming contributions from anyone interested in improving continuwuity.
What is Matrix?
Matrix is an open, federated, and extensible network for decentralized communication. Users from any Matrix homeserver can chat with users from all other homeservers over federation. Matrix is designed to be extensible and built on top of. You can even use bridges such as Matrix Appservices to communicate with users outside of Matrix, like a community on Discord.
What are the project's goals?
Continuwuity aims to:
- Maintain a stable, reliable Matrix homeserver implementation in Rust
- Improve compatibility and specification compliance with the Matrix protocol
- Fix bugs and performance issues from the original conduwuit
- Add missing features needed by homeserver administrators
- Provide comprehensive documentation and easy deployment options
- Create a sustainable development model for long-term maintenance
- Keep a lightweight, efficient codebase that can run on modest hardware
Can I try it out?
Check out the documentation for installation instructions.
If you want to try it out as a user, we have some partnered homeservers you can use:
-
You can head over to https://federated.nexus in your browser.
- Hit the
Apply to Joinbutton. Once your request has been accepted, you will receive an email with your username and password. - Head over to https://app.federated.nexus and you can sign in there, or use any other matrix chat client you wish elsewhere.
- Your username for matrix will be in the form of
@username:federated.nexus, however you can simply use theusernamepart to log in. Your password is your password.
- Hit the
-
There's also https://continuwuity.rocks/. You can register a new account using Cinny via this convenient link, or you can use Element or another matrix client that supports registration.
What are we working on?
We're working our way through all of the issues in the Forgejo project.
- Packaging & availability in more places
- Appservices bugs & features
- Improving compatibility and spec compliance
- Automated testing
- Admin API
- Policy-list controlled moderation
Can I migrate my data from x?
- Conduwuit: Yes
- Conduit: No, database is now incompatible
- Grapevine: No, database is now incompatible
- Dendrite: No
- Synapse: No
We haven't written up a guide on migrating from incompatible homeservers yet. Reach out to us if you need to do this!
Contribution
Development flow
- Features / changes must developed in a separate branch
- For each change, create a descriptive PR
- Your code will be reviewed by one or more of the continuwuity developers
- The branch will be deployed live on multiple tester's matrix servers to shake out bugs
- Once all testers and reviewers have agreed, the PR will be merged to the main branch
- The main branch will have nightly builds deployed to users on the cutting edge
- Every week or two, a new release is cut.
The main branch is always green!
Policy on pulling from other forks
We welcome contributions from other forks of conduwuit, subject to our review process. When incorporating code from other forks:
- All external contributions must go through our standard PR process
- Code must meet our quality standards and pass tests
- Code changes will require testing on multiple test servers before merging
- Attribution will be given to original authors and forks
- We prioritize stability and compatibility when evaluating external contributions
- Features that align with our project goals will be given priority consideration
Contact
Join our Matrix room and space to chat with us about the project!