Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[env]
DHTTP_BOOTSTRAP_URL = "https://bootstrap.genmeta.net:20002"
DHTTP_H3_DNS_SERVER = "https://ddns.genmeta.net:4433"
DHTTP_MDNS_SERVICE = "_dhttp.local"
24 changes: 4 additions & 20 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
# Copy this file to .env and load it in your shell or CI before building.
# Pishoo does not load .env files automatically.

DHTTP_BOOTSTRAP_URL=https://bootstrap.genmeta.net
# Optional shell/CI overrides. Cargo defaults to the same production values.
DHTTP_BOOTSTRAP_URL=https://bootstrap.genmeta.net:20002
DHTTP_CERT_SERVER_URL=https://api.genmeta.net:4433
DHTTP_H3_DNS_SERVER=https://ddns.genmeta.net:4433
DHTTP_MDNS_SERVICE="_dhttp.local"
DHTTP_ROOT_CA_PEM="-----BEGIN CERTIFICATE-----
MIICVTCCAdqgAwIBAgIUcBI2Xq2ZS+SYgLrPjqN0eqCf5a8wCgYIKoZIzj0EAwMw
WTELMAkGA1UEBhMCQ04xETAPBgNVBAgMCEhvbmdLb25nMRwwGgYDVQQKDBNHZW5t
ZXRhIEVDQyBSb290IENBMRkwFwYDVQQDDBByb290Lmdlbm1ldGEubmV0MB4XDTI1
MDQyMTA1MDEwNFoXDTQ1MDQxNjA1MDEwNFowWTELMAkGA1UEBhMCQ04xETAPBgNV
BAgMCEhvbmdLb25nMRwwGgYDVQQKDBNHZW5tZXRhIEVDQyBSb290IENBMRkwFwYD
VQQDDBByb290Lmdlbm1ldGEubmV0MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEA81Y
m6nFz+c58mDquLk2KhZufUejy6js+Ru9AsrRyFIHCHbTQSOlkK+QXZMqRBkBSA+n
Gy0mnkf0zeXj9NFqTYveIrgDtXv/WoD3eadAyD8CAu5O0/XsyQGaP/bS+sEeo2Mw
YTAdBgNVHQ4EFgQUXgbKWqQlEhGkIdqW+slZ0wTtCS8wHwYDVR0jBBgwFoAUXgbK
WqQlEhGkIdqW+slZ0wTtCS8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwCgYIKoZIzj0EAwMDaQAwZgIxALCdmweGeSRn+B5IblH1emNjT2kw3CKO8l1g
jYBag7kqGK8ZGjwx9xpC5QMn3hw8qQIxAKKnzCktBfssKJ5HMEHuWCg0rw/FHmpu
PrFCruzsQiLcBa+GAP2O7Qbl+XAlK+MNiA==
-----END CERTIFICATE-----"
DHTTP_MDNS_SERVICE=_dhttp.local
18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "3"
members = ["dhttp", "identity", "home", "api", "access", "log"]

[workspace.package]
version = "0.6.0-beta.5"
version = "0.6.0"
edition = "2024"
license = "Apache-2.0"
repository = "https://github.com/genmeta/dhttp"
Expand Down Expand Up @@ -41,20 +41,20 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["f
# Keep same-repository workspace members as path dependencies, except staged
# dhttp-identity releases, which resolve through crates.io to avoid duplicate
# identity types with h3x/ddns in the formal release graph.
dhttp-identity = "0.3.0-beta.1"
dhttp-home = { path = "home", version = "0.5.0-beta.1" }
dquic = { version = "0.7.0-beta.4", default-features = false }
ddns = { package = "dyns", version = "0.7.0-beta.2", features = [
dhttp-identity = "0.3.0"
dhttp-home = { path = "home", version = "0.5.0" }
dquic = { version = "0.7.0", default-features = false }
ddns = { package = "dyns", version = "0.7.0", features = [
"resolvers",
"publishers",
"h3",
"http",
"mdns",
"dquic-network",
] }
h3x = { version = "0.6.0-beta.4", features = [
h3x = { version = "0.6.0", features = [
"dquic",
] }
dhttp = { path = "dhttp", version = "0.6.0-beta.5" }
dhttp-access = { path = "access", version = "0.4.0-beta.2" }
dhttp-log = { path = "log", version = "0.1.0-beta.1" }
dhttp = { path = "dhttp", version = "0.6.0" }
dhttp-access = { path = "access", version = "0.4.0" }
dhttp-log = { path = "log", version = "0.1.0" }
2 changes: 1 addition & 1 deletion access/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "dhttp-access"
description = "Identity-aware access control primitives for DHttp"
version = "0.4.0-beta.2"
version = "0.4.0"
edition.workspace = true
license.workspace = true
repository.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion api/js/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type FetchHandler = (request: DhttpRequest) => Response | Promise<Respons
export type RawHandler = (
request: import("@genmeta/dhttp/raw").UnresolvedRequest,
) => void | Promise<void>;
export type CertificateChainKind = "primary" | "secondary";
export type CertificateChainKind = "client" | "client and server";

export interface CertificateChainKey {
sequence: number;
Expand Down
4 changes: 2 additions & 2 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@genmeta/dhttp",
"version": "0.6.0-beta.5",
"version": "0.6.0",
"description": "The True Internet",
"license": "Apache-2.0",
"homepage": "https://dhttp.net/",
Expand Down
2 changes: 1 addition & 1 deletion api/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl From<dhttp::certificate::DhttpSubjectKeyIdentifier> for DhttpSubjectKeyIden
value: value.to_string(),
chain: CertificateChainKey {
sequence: value.chain().sequence().get(),
kind: value.chain().kind().as_str().to_owned(),
kind: value.chain().usage().as_str().to_owned(),
},
owner_hash: value.owner_hash().as_str().to_owned(),
}
Expand Down
3 changes: 3 additions & 0 deletions dhttp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ dquic = { workspace = true }
ddns = { workspace = true }
h3x = { workspace = true }

[build-dependencies]
url = "2"

[dev-dependencies]
dhttp-access = { workspace = true, features = ["http", "orm"] }
tokio = { workspace = true, features = ["macros", "rt"] }
93 changes: 66 additions & 27 deletions dhttp/build.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
use std::{env, fs, path::PathBuf};

const ROOT_CA_ENV: &str = "DHTTP_ROOT_CA";
const STUN_SERVER_ENV: &str = "DHTTP_STUN_SERVER";
const BOOTSTRAP_URL_ENV: &str = "DHTTP_BOOTSTRAP_URL";

const DEFAULT_STUN_SERVER: &str = "stun.dhttp.example.net";
const DEFAULT_BOOTSTRAP_URL: &str = "https://bootstrap.genmeta.net:20002";
const DEFAULT_ROOT_CA_PEM: &str = "\
-----BEGIN CERTIFICATE-----\n\
MIIDKTCCAhGgAwIBAgIUHNScq6R2U5QYUzxkEkNDaOJt4yMwDQYJKoZIhvcNAQEL\n\
BQAwHDEaMBgGA1UEAwwRZGh0dHAuZXhhbXBsZS5uZXQwHhcNMjYwNjA0MTE0NjI1\n\
WhcNMzYwNjAxMTE0NjI1WjAcMRowGAYDVQQDDBFkaHR0cC5leGFtcGxlLm5ldDCC\n\
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCHw/okfV4RDIN5ws0IMGi1\n\
c1TTQAklaBynw8neIM+ZniGLOluRLvYATY4xla/ayjYdzXmel+4hdaG5pN6Ggdgm\n\
PhIj9hpsNKOjjkBHtdnDg/Dfc/L+ElRDPMNosvVN2L/G4yDBso5SGJUlFE7Fww3x\n\
izrTCCbR4lofwZAnUZAtvnX3KGgvgRtg0VdgvpWJ1JOnwQYm1qU6ljFG+72418SW\n\
Htn2A8SRGSO1Im1W1QMY4OJtAVwfi/XwwN9ifWWPwcl4OGV/MXBLUQRF4gzgFC+1\n\
8EG5jKCZtoJcY3FY+LF1DMkdbmE6TOddycev0HWNBPQtSMsxTo9aYrApKnOk5ekC\n\
AwEAAaNjMGEwHQYDVR0OBBYEFDe4aYWivTEYa1hkBy7WaZOVY/S6MB8GA1UdIwQY\n\
MBaAFDe4aYWivTEYa1hkBy7WaZOVY/S6MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P\n\
AQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAEpkndNISW40WGLJa9QVYcBAyP\n\
Ou+vdoRuch+/jeIyM4Lau2yBYu1nyneLmVpJGTCFHxcTrYCmUqJKnkWbHPXoDY27\n\
MPGih8GkiIW057JUbcyktKZqzzmsAoCDz6kLp76BpJiLViiSctufWKeHt8Rm/n66\n\
dI2XmJcJxJ9Xs+QkziinZbiUKQxiDBTgkTT4kxhMESlb4iV9YNpIvW4m8fqCaxtl\n\
ECyJBmqcGIp3bEYchL4hs8I3jbT3VXykjCK/FU/wLWSCIMzhsV4U7JxI0xKGziPU\n\
p83DC7/YpVF7avt05Mwb0n3RhKSmDMLnfWs61zTDQu0T/IYBbr+PvVMsw+W4\n\
MIICVzCCAd2gAwIBAgIUe8kwBACY6f+MAzdCBVPmq4p+CiswCgYIKoZIzj0EAwMw\n\
WTELMAkGA1UEBhMCQ04xETAPBgNVBAgMCEhvbmdLb25nMRwwGgYDVQQKDBNHZW5t\n\
ZXRhIEVDQyBSb290IENBMRkwFwYDVQQDDBByb290Lmdlbm1ldGEubmV0MB4XDTI2\n\
MDcxMzEzMDQyOFoXDTQ2MDcxMzEzMDQyOFowWTELMAkGA1UEBhMCQ04xETAPBgNV\n\
BAgMCEhvbmdLb25nMRwwGgYDVQQKDBNHZW5tZXRhIEVDQyBSb290IENBMRkwFwYD\n\
VQQDDBByb290Lmdlbm1ldGEubmV0MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEO+zm\n\
ZYL0LaqTKf7mW4tnRWeNop1p8f2ZsexhAl23GHkHwLjCihhQzBCZ8VMRPAdVcEIS\n\
XcGY/U6+Z1IAYCRG0tdsUCXHXxzvDY0I9FZqZw1Xo94gkHnNe7mTu/jCQg3Xo2Yw\n\
ZDAdBgNVHQ4EFgQUq1SsSWDnp0G5v5/hWi9CC7eWDTwwHwYDVR0jBBgwFoAUq1Ss\n\
SWDnp0G5v5/hWi9CC7eWDTwwEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8E\n\
BAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwK9GqxdmRHJw7iB0z/b/WgzBv2jb7OmFS\n\
uVPA+6ZNApjYXCZUOVQFC60KUUV7yW53AjEA5lLrdXxdGNSIuLe1h/A+v/vRrYtt\n\
132Jzh+LkKBHdC1wcvDKjk2ZQG5WySly6VMp\n\
-----END CERTIFICATE-----\n";

fn main() {
Expand All @@ -37,17 +33,21 @@ fn main() {
)
});

let stun_server = env_or_default(STUN_SERVER_ENV, DEFAULT_STUN_SERVER);
let bootstrap_url = env_or_default(BOOTSTRAP_URL_ENV, DEFAULT_BOOTSTRAP_URL);
let bootstrap_authority = bootstrap_authority(&bootstrap_url).unwrap_or_else(|error| {
panic!("invalid {BOOTSTRAP_URL_ENV} value {bootstrap_url:?}: {error}")
});
let bootstrap = format!(
"// @generated by build.rs; do not edit.\n\
pub const DHTTP_STUN_SERVER: &str = {stun_server:?};\n\
pub const DHTTP_BOOTSTRAP_URL: &str = {bootstrap_url:?};\n\
pub const DHTTP_BOOTSTRAP_AUTHORITY: &str = {bootstrap_authority:?};\n\
pub const DHTTP_ROOT_CA: &[u8] = &{root_ca:?};\n"
);
fs::write(out_dir.join("bootstrap.rs"), bootstrap)
.expect("failed to write generated DHTTP bootstrap constants");

println!("cargo::rerun-if-env-changed={ROOT_CA_ENV}");
println!("cargo::rerun-if-env-changed={STUN_SERVER_ENV}");
println!("cargo::rerun-if-env-changed={BOOTSTRAP_URL_ENV}");
if let Some(root_ca) = optional_env_path(ROOT_CA_ENV) {
println!("cargo::rerun-if-changed={}", root_ca.display());
}
Expand All @@ -57,6 +57,31 @@ fn env_or_default(name: &str, default: &str) -> String {
env::var(name).unwrap_or_else(|_| default.to_owned())
}

fn bootstrap_authority(value: &str) -> Result<String, String> {
let url = url::Url::parse(value).map_err(|error| error.to_string())?;
if url.scheme() != "https" {
return Err("scheme must be https".to_owned());
}
if url.username() != "" || url.password().is_some() {
return Err("credentials are not allowed".to_owned());
}
if url.path() != "/" || url.query().is_some() || url.fragment().is_some() {
return Err("path, query, and fragment are not allowed".to_owned());
}

let host = url
.host_str()
.ok_or_else(|| "host is required".to_owned())?;
let port = url
.port()
.ok_or_else(|| "an explicit port is required".to_owned())?;
if matches!(url.host(), Some(url::Host::Ipv6(_))) {
Ok(format!("[{host}]:{port}"))
} else {
Ok(format!("{host}:{port}"))
}
}

fn optional_env_path(name: &str) -> Option<PathBuf> {
env::var_os(name).map(PathBuf::from)
}
Expand All @@ -79,17 +104,31 @@ mod tests {
use super::*;

#[test]
fn missing_stun_env_uses_dhttp_example_net_placeholder() {
let name = format!("__DHTTP_MISSING_STUN_{}", std::process::id());
fn missing_bootstrap_env_uses_genmeta_production_default() {
let name = format!("__DHTTP_MISSING_BOOTSTRAP_{}", std::process::id());

assert_eq!(
env_or_default(&name, DEFAULT_STUN_SERVER),
"stun.dhttp.example.net"
env_or_default(&name, DEFAULT_BOOTSTRAP_URL),
"https://bootstrap.genmeta.net:20002"
);
}

#[test]
fn placeholder_root_ca_is_pem_certificate() {
fn bootstrap_url_produces_stun_authority() {
assert_eq!(
bootstrap_authority("https://bootstrap.genmeta.net:20002").as_deref(),
Ok("bootstrap.genmeta.net:20002")
);
}

#[test]
fn bootstrap_url_requires_https_and_explicit_port() {
assert!(bootstrap_authority("http://bootstrap.genmeta.net:20002").is_err());
assert!(bootstrap_authority("https://bootstrap.genmeta.net").is_err());
}

#[test]
fn default_root_ca_is_pem_certificate() {
assert!(DEFAULT_ROOT_CA_PEM.starts_with("-----BEGIN CERTIFICATE-----"));
assert!(DEFAULT_ROOT_CA_PEM.ends_with("-----END CERTIFICATE-----\n"));
}
Expand Down
Loading
Loading