separate host/port columns, increase host len to 1024 bytes

This commit is contained in:
yggverse 2025-07-23 08:15:48 +03:00
parent a7230fd329
commit 77ee4aa78c
3 changed files with 51 additions and 39 deletions

View File

@ -4,7 +4,10 @@ mod database;
use anyhow::Result;
use certificate::Certificate;
use database::Database;
use gtk::{gio::TlsCertificate, glib::Uri};
use gtk::{
gio::TlsCertificate,
glib::{GString, Uri},
};
use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
@ -15,7 +18,7 @@ use std::{cell::RefCell, collections::HashMap};
/// https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation
pub struct Tofu {
database: Database,
memory: RefCell<HashMap<String, Certificate>>,
memory: RefCell<HashMap<(GString, i32), Certificate>>,
}
impl Tofu {
@ -31,8 +34,11 @@ impl Tofu {
// build in-memory index...
let mut m = memory.borrow_mut();
for r in records {
if m.insert(r.address, Certificate::from_db(Some(r.id), &r.pem, r.time)?)
.is_some()
if m.insert(
(r.host.into(), r.port),
Certificate::from_db(Some(r.id), &r.pem, r.time)?,
)
.is_some()
{
panic!() // expect unique address
}
@ -50,31 +56,34 @@ impl Tofu {
default_port: i32,
tls_certificate: TlsCertificate,
) -> Result<bool> {
match address(uri, default_port) {
Some(k) => Ok(self
match uri.host() {
Some(host) => Ok(self
.memory
.borrow_mut()
.insert(k, Certificate::from_tls_certificate(tls_certificate)?)
.insert(
(host, port(uri.port(), default_port)),
Certificate::from_tls_certificate(tls_certificate)?,
)
.is_none()),
None => Ok(false),
}
}
pub fn server_certificate(&self, uri: &Uri, default_port: i32) -> Option<TlsCertificate> {
address(uri, default_port).and_then(|k| {
uri.host().and_then(|host| {
self.memory
.borrow()
.get(&k)
.get(&(host, port(uri.port(), default_port)))
.map(|c| c.tls_certificate().clone())
})
}
/// Save in-memory index to the permanent database (on app close)
pub fn save(&self) -> Result<()> {
for (address, certificate) in self.memory.borrow_mut().drain() {
for ((host, port), certificate) in self.memory.borrow_mut().drain() {
if certificate.id().is_none() {
self.database
.add(address, certificate.time(), &certificate.pem())?;
.add(host.into(), port, certificate.time(), &certificate.pem())?;
}
}
Ok(())
@ -94,17 +103,10 @@ pub fn migrate(tx: &Transaction) -> Result<()> {
Ok(())
}
fn address(uri: &Uri, default_port: i32) -> Option<String> {
uri.host().map(|host| {
let port = uri.port();
format!(
"{}:{}",
host,
if port.is_positive() {
port
} else {
default_port
}
)
})
fn port(port: i32, default_port: i32) -> i32 {
if port.is_positive() {
port
} else {
default_port
}
}

View File

@ -34,10 +34,10 @@ impl Database {
/// Create new record in database
/// * return last insert ID on success
pub fn add(&self, address: String, time: &DateTime, pem: &str) -> Result<i64> {
pub fn add(&self, host: String, port: i32, time: &DateTime, pem: &str) -> Result<i64> {
let mut connection = self.pool.get()?;
let tx = connection.transaction()?;
let id = insert(&tx, self.profile_id, address, time, pem)?;
let id = insert(&tx, self.profile_id, host, port, time, pem)?;
tx.commit()?;
Ok(id)
}
@ -52,11 +52,12 @@ pub fn init(tx: &Transaction) -> Result<usize> {
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`profile_id` INTEGER NOT NULL,
`time` INTEGER NOT NULL,
`address` VARCHAR(255) NOT NULL,
`port` INTEGER NOT NULL,
`host` VARCHAR(1024) NOT NULL,
`pem` TEXT NOT NULL,
FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`),
UNIQUE (`address`)
UNIQUE (`host`, `port`)
)",
[],
)?)
@ -65,7 +66,8 @@ pub fn init(tx: &Transaction) -> Result<usize> {
pub fn insert(
tx: &Transaction,
profile_id: i64,
address: String,
host: String,
port: i32,
time: &DateTime,
pem: &str,
) -> Result<i64> {
@ -73,28 +75,35 @@ pub fn insert(
"INSERT INTO `profile_tofu` (
`profile_id`,
`time`,
`address`,
`host`,
`port`,
`pem`
) VALUES (?, ?, ?, ?) ON CONFLICT (`address`)
DO UPDATE SET `time` = `excluded`.`time`,
`pem` = `excluded`.`pem`",
(profile_id, time.to_unix(), address, pem),
) VALUES (?, ?, ?, ?, ?) ON CONFLICT (`host`, `port`)
DO UPDATE SET `time` = `excluded`.`time`,
`pem` = `excluded`.`pem`",
(profile_id, time.to_unix(), host, port, pem),
)?;
Ok(tx.last_insert_rowid())
}
pub fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Row>> {
let mut stmt = tx.prepare(
"SELECT `id`, `profile_id`, `address`, `time`, `pem` FROM `profile_tofu` WHERE `profile_id` = ?",
"SELECT `id`,
`profile_id`,
`host`,
`port`,
`time`,
`pem` FROM `profile_tofu` WHERE `profile_id` = ?",
)?;
let result = stmt.query_map([profile_id], |row| {
Ok(Row {
id: row.get(0)?,
//profile_id: row.get(1)?,
address: row.get(2)?,
time: DateTime::from_unix_local(row.get(3)?).unwrap(),
pem: row.get(4)?,
host: row.get(2)?,
port: row.get(3)?,
time: DateTime::from_unix_local(row.get(4)?).unwrap(),
pem: row.get(5)?,
})
})?;

View File

@ -1,6 +1,7 @@
pub struct Row {
pub address: String,
pub host: String,
pub id: i64,
pub pem: String,
pub port: i32,
pub time: gtk::glib::DateTime,
}