Adding upstream version 0.0.22.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
2f814b513a
commit
b06d3acde8
190 changed files with 61565 additions and 0 deletions
1012
icann-rdap-srv/src/storage/data.rs
Normal file
1012
icann-rdap-srv/src/storage/data.rs
Normal file
File diff suppressed because it is too large
Load diff
8
icann-rdap-srv/src/storage/mem/config.rs
Normal file
8
icann-rdap-srv/src/storage/mem/config.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use buildstructor::Builder;
|
||||
|
||||
use crate::storage::CommonConfig;
|
||||
|
||||
#[derive(Debug, Builder, Clone)]
|
||||
pub struct MemConfig {
|
||||
pub common_config: CommonConfig,
|
||||
}
|
365
icann-rdap-srv/src/storage/mem/label_search.rs
Normal file
365
icann-rdap-srv/src/storage/mem/label_search.rs
Normal file
|
@ -0,0 +1,365 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use {ab_radix_trie::Trie, buildstructor::Builder};
|
||||
|
||||
use crate::error::RdapServerError;
|
||||
|
||||
/// A structure for searching DNS labels as specified in RFC 9082.
|
||||
/// For RDAP, type T is likely RdapResponse or Arc<RdapResponse>.
|
||||
#[derive(Builder)]
|
||||
pub struct SearchLabels<T: Clone> {
|
||||
label_suffixes: HashMap<String, Trie<T>>,
|
||||
}
|
||||
|
||||
impl<T: Clone> SearchLabels<T> {
|
||||
/// Insert a value based on a domain name.
|
||||
pub(crate) fn insert(&mut self, text: &str, value: T) {
|
||||
// char_indices gets the UTF8 indices as well as the character
|
||||
for (i, char) in text.char_indices() {
|
||||
if char == '.' && i != 0 {
|
||||
let prefix = &text[..i];
|
||||
// find the next UTF8 character index
|
||||
let mut next_i = i + 1;
|
||||
while !text.is_char_boundary(next_i) {
|
||||
next_i += 1;
|
||||
}
|
||||
let suffix = &text[next_i..];
|
||||
self.label_suffixes
|
||||
.entry(suffix.to_owned())
|
||||
.or_insert(Trie::new())
|
||||
.insert(prefix, Some(value.clone()));
|
||||
}
|
||||
}
|
||||
// the root
|
||||
self.label_suffixes
|
||||
.entry(String::default())
|
||||
.or_insert(Trie::new())
|
||||
.insert(text, Some(value.clone()));
|
||||
}
|
||||
|
||||
/// Search values based on a label search
|
||||
pub(crate) fn search(&self, search: &str) -> Result<Vec<T>, RdapServerError> {
|
||||
// search string is invalid if it doesn't have only one asterisk ('*')
|
||||
if search.chars().filter(|c| *c == '*').count() != 1 {
|
||||
return Err(RdapServerError::InvalidArg(
|
||||
"Search string must contain one and only one asterisk ('*')".to_string(),
|
||||
));
|
||||
}
|
||||
// asterisk must not be followed by a character other than dot ('.')
|
||||
let star = search
|
||||
.find('*')
|
||||
.expect("internal error. previous check should have caught this");
|
||||
if star != search.chars().count() - 1
|
||||
&& search
|
||||
.chars()
|
||||
.nth(star + 1)
|
||||
.expect("should have been short circuited")
|
||||
!= '.'
|
||||
{
|
||||
return Err(RdapServerError::InvalidArg(
|
||||
"Search string asterisk ('*') must terminate domain label".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let parts = search
|
||||
.split_once('*')
|
||||
.expect("internal error. previous check should insure there is an asterisk");
|
||||
|
||||
// this is a limitation of the trie in that it requires a prefix
|
||||
if parts.0.is_empty() {
|
||||
return Err(RdapServerError::InvalidArg(
|
||||
"Search string must have a prefix".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(trie) = self.label_suffixes.get(parts.1.trim_start_matches('.')) {
|
||||
if let Some(entries) = trie.get_suffixes_values(parts.0) {
|
||||
if !entries.is_empty() {
|
||||
let values = entries
|
||||
.iter()
|
||||
.filter_map(|e| e.val.clone())
|
||||
.collect::<Vec<T>>();
|
||||
return Ok(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod tests {
|
||||
|
||||
use ab_radix_trie::{Entry, Trie};
|
||||
|
||||
use super::SearchLabels;
|
||||
|
||||
#[test]
|
||||
fn GIVEN_domain_names_WHEN_inserting_THEN_search_labels_is_correct() {
|
||||
// GIVEN
|
||||
let mut search = SearchLabels::builder().build();
|
||||
|
||||
// WHEN
|
||||
search.insert("foo.example.com", "foo.example.com".to_owned());
|
||||
search.insert("bar.example.com", "bar.example.com".to_owned());
|
||||
search.insert("foo.example.net", "foo.example.net".to_owned());
|
||||
search.insert("bar.example.net", "bar.example.net".to_owned());
|
||||
|
||||
// THEN
|
||||
dbg!(&search.label_suffixes);
|
||||
assert_eq!(search.label_suffixes.len(), 5);
|
||||
// root
|
||||
let root = search.label_suffixes.get("").expect("no root");
|
||||
assert_trie(
|
||||
root,
|
||||
"foo.example.",
|
||||
&["foo.example.com", "foo.example.net"],
|
||||
&["bar.example.com", "bar.example.net"],
|
||||
);
|
||||
assert_trie(
|
||||
root,
|
||||
"bar.example.",
|
||||
&["bar.example.com", "bar.example.net"],
|
||||
&["foo.example.com", "foo.example.net"],
|
||||
);
|
||||
// com
|
||||
let com = search.label_suffixes.get("com").expect("no trie");
|
||||
assert_trie(
|
||||
com,
|
||||
"foo.example",
|
||||
&["foo.example.com"],
|
||||
&["bar.example.com", "bar.example.net", "foo.example.net"],
|
||||
);
|
||||
assert_trie(
|
||||
com,
|
||||
"bar.example",
|
||||
&["bar.example.com"],
|
||||
&["foo.example.com", "foo.example.net", "bar.example.net"],
|
||||
);
|
||||
// net
|
||||
let net = search.label_suffixes.get("net").expect("no trie");
|
||||
assert_trie(
|
||||
net,
|
||||
"foo.example",
|
||||
&["foo.example.net"],
|
||||
&["bar.example.net", "bar.example.com", "foo.example.com"],
|
||||
);
|
||||
assert_trie(
|
||||
net,
|
||||
"bar.example",
|
||||
&["bar.example.net"],
|
||||
&["foo.example.com", "foo.example.net", "bar.example.com"],
|
||||
);
|
||||
// example.com
|
||||
let example_com = search.label_suffixes.get("example.com").expect("no trie");
|
||||
assert_trie(
|
||||
example_com,
|
||||
"foo",
|
||||
&["foo.example.com"],
|
||||
&["bar.example.com", "bar.example.net", "foo.example.net"],
|
||||
);
|
||||
assert_trie(
|
||||
example_com,
|
||||
"bar",
|
||||
&["bar.example.com"],
|
||||
&["foo.example.com", "foo.example.net", "bar.example.net"],
|
||||
);
|
||||
// example.net
|
||||
let example_net = search.label_suffixes.get("example.net").expect("no trie");
|
||||
assert_trie(
|
||||
example_net,
|
||||
"foo",
|
||||
&["foo.example.net"],
|
||||
&["bar.example.net", "bar.example.com", "foo.example.com"],
|
||||
);
|
||||
assert_trie(
|
||||
example_net,
|
||||
"bar",
|
||||
&["bar.example.net"],
|
||||
&["foo.example.com", "foo.example.net", "bar.example.com"],
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_trie(trie: &Trie<String>, suffix: &str, must_have: &[&str], must_not_have: &[&str]) {
|
||||
let entries = trie
|
||||
.get_suffixes_values(suffix)
|
||||
.expect("no values in entries");
|
||||
for s in must_have {
|
||||
assert!(
|
||||
trie_contains(&entries, s),
|
||||
"suffix = {suffix} did not find {s}"
|
||||
);
|
||||
}
|
||||
for s in must_not_have {
|
||||
assert!(!trie_contains(&entries, s), "suffix = {suffix} found {s}");
|
||||
}
|
||||
}
|
||||
|
||||
fn trie_contains(entries: &[Entry<'_, String>], value: &str) -> bool {
|
||||
entries
|
||||
.iter()
|
||||
.any(|e| e.val.as_ref().expect("no entry value") == value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_search_string_with_two_asterisks_WHEN_search_THEN_error() {
|
||||
// GIVEN
|
||||
let labels: SearchLabels<String> = SearchLabels::builder().build();
|
||||
let search = "foo.*.*";
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search(search);
|
||||
|
||||
// THEN
|
||||
assert!(actual.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_search_string_with_asterisk_suffix_WHEN_search_THEN_error() {
|
||||
// GIVEN
|
||||
let labels: SearchLabels<String> = SearchLabels::builder().build();
|
||||
let search = "foo.*example.net";
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search(search);
|
||||
|
||||
// THEN
|
||||
assert!(actual.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_search_string_with_no_asterisk_WHEN_search_THEN_error() {
|
||||
// GIVEN
|
||||
let labels: SearchLabels<String> = SearchLabels::builder().build();
|
||||
let search = "foo.example.net";
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search(search);
|
||||
|
||||
// THEN
|
||||
assert!(actual.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_empty_search_string_WHEN_search_THEN_error() {
|
||||
// GIVEN
|
||||
let labels: SearchLabels<String> = SearchLabels::builder().build();
|
||||
let search = "";
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search(search);
|
||||
|
||||
// THEN
|
||||
assert!(actual.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_root_search_WHEN_search_THEN_correct_values_found() {
|
||||
// GIVEN
|
||||
let mut labels = SearchLabels::builder().build();
|
||||
labels.insert("foo.example.com", "foo.example.com".to_owned());
|
||||
labels.insert("bar.example.com", "bar.example.com".to_owned());
|
||||
labels.insert("foo.example.net", "foo.example.net".to_owned());
|
||||
labels.insert("bar.example.net", "bar.example.net".to_owned());
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search("foo.example.*").expect("search is invalid");
|
||||
|
||||
// THEN
|
||||
dbg!(&actual);
|
||||
assert_eq!(actual.len(), 2);
|
||||
assert!(actual.contains(&"foo.example.com".to_string()));
|
||||
assert!(actual.contains(&"foo.example.net".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_root_search_WHEN_search_with_prefix_THEN_correct_values_found() {
|
||||
// GIVEN
|
||||
let mut labels = SearchLabels::builder().build();
|
||||
labels.insert("foo.example.com", "foo.example.com".to_owned());
|
||||
labels.insert("bar.example.com", "bar.example.com".to_owned());
|
||||
labels.insert("foo.example.net", "foo.example.net".to_owned());
|
||||
labels.insert("bar.example.net", "bar.example.net".to_owned());
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search("foo.example.n*").expect("search is invalid");
|
||||
|
||||
// THEN
|
||||
dbg!(&actual);
|
||||
assert_eq!(actual.len(), 1);
|
||||
assert!(actual.contains(&"foo.example.net".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_labels_WHEN_sld_search_with_prefix_THEN_correct_values_found() {
|
||||
// GIVEN
|
||||
let mut labels = SearchLabels::builder().build();
|
||||
labels.insert("foo.example.com", "foo.example.com".to_owned());
|
||||
labels.insert("bar.example.com", "bar.example.com".to_owned());
|
||||
labels.insert("foo.example.net", "foo.example.net".to_owned());
|
||||
labels.insert("bar.example.net", "bar.example.net".to_owned());
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search("foo.ex*.com").expect("search is invalid");
|
||||
|
||||
// THEN
|
||||
dbg!(&actual);
|
||||
assert_eq!(actual.len(), 1);
|
||||
assert!(actual.contains(&"foo.example.com".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_labels_WHEN_3ld_search_with_prefix_THEN_correct_values_found() {
|
||||
// GIVEN
|
||||
let mut labels = SearchLabels::builder().build();
|
||||
labels.insert("foo.example.com", "foo.example.com".to_owned());
|
||||
labels.insert("bar.example.com", "bar.example.com".to_owned());
|
||||
labels.insert("foo.example.net", "foo.example.net".to_owned());
|
||||
labels.insert("bar.example.net", "bar.example.net".to_owned());
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search("fo*.example.com").expect("search is invalid");
|
||||
|
||||
// THEN
|
||||
dbg!(&actual);
|
||||
assert_eq!(actual.len(), 1);
|
||||
assert!(actual.contains(&"foo.example.com".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_labels_WHEN_sld_search_THEN_correct_values_found() {
|
||||
// GIVEN
|
||||
let mut labels = SearchLabels::builder().build();
|
||||
labels.insert("foo.example.com", "foo.example.com".to_owned());
|
||||
labels.insert("bar.example.com", "bar.example.com".to_owned());
|
||||
labels.insert("foo.example.net", "foo.example.net".to_owned());
|
||||
labels.insert("bar.example.net", "bar.example.net".to_owned());
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search("foo.*.com").expect("search is invalid");
|
||||
|
||||
// THEN
|
||||
dbg!(&actual);
|
||||
assert_eq!(actual.len(), 1);
|
||||
assert!(actual.contains(&"foo.example.com".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn GIVEN_labels_WHEN_3ld_search_THEN_error() {
|
||||
// GIVEN
|
||||
let mut labels = SearchLabels::builder().build();
|
||||
labels.insert("foo.example.com", "foo.example.com".to_owned());
|
||||
labels.insert("bar.example.com", "bar.example.com".to_owned());
|
||||
labels.insert("foo.example.net", "foo.example.net".to_owned());
|
||||
labels.insert("bar.example.net", "bar.example.net".to_owned());
|
||||
|
||||
// WHEN
|
||||
let actual = labels.search("*.example.com");
|
||||
|
||||
// THEN
|
||||
dbg!(&actual);
|
||||
assert!(actual.is_err());
|
||||
}
|
||||
}
|
6
icann-rdap-srv/src/storage/mem/mod.rs
Normal file
6
icann-rdap-srv/src/storage/mem/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
#![allow(dead_code)] // TODO remove
|
||||
|
||||
pub mod config;
|
||||
mod label_search;
|
||||
pub mod ops;
|
||||
pub mod tx;
|
201
icann-rdap-srv/src/storage/mem/ops.rs
Normal file
201
icann-rdap-srv/src/storage/mem/ops.rs
Normal file
|
@ -0,0 +1,201 @@
|
|||
use std::{collections::HashMap, net::IpAddr, str::FromStr, sync::Arc};
|
||||
|
||||
use {
|
||||
async_trait::async_trait,
|
||||
btree_range_map::RangeMap,
|
||||
icann_rdap_common::{
|
||||
prelude::ToResponse,
|
||||
response::{Domain, DomainSearchResults, RdapResponse},
|
||||
},
|
||||
ipnet::{IpNet, Ipv4Net, Ipv6Net},
|
||||
prefix_trie::PrefixMap,
|
||||
tokio::sync::RwLock,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::RdapServerError,
|
||||
rdap::response::{NOT_FOUND, NOT_IMPLEMENTED},
|
||||
storage::{CommonConfig, StoreOps, TxHandle},
|
||||
};
|
||||
|
||||
use super::{config::MemConfig, label_search::SearchLabels, tx::MemTx};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Mem {
|
||||
pub(crate) autnums: Arc<RwLock<RangeMap<u32, Arc<RdapResponse>>>>,
|
||||
pub(crate) ip4: Arc<RwLock<PrefixMap<Ipv4Net, Arc<RdapResponse>>>>,
|
||||
pub(crate) ip6: Arc<RwLock<PrefixMap<Ipv6Net, Arc<RdapResponse>>>>,
|
||||
pub(crate) domains: Arc<RwLock<HashMap<String, Arc<RdapResponse>>>>,
|
||||
pub(crate) domains_by_name: Arc<RwLock<SearchLabels<Arc<RdapResponse>>>>,
|
||||
pub(crate) idns: Arc<RwLock<HashMap<String, Arc<RdapResponse>>>>,
|
||||
pub(crate) nameservers: Arc<RwLock<HashMap<String, Arc<RdapResponse>>>>,
|
||||
pub(crate) entities: Arc<RwLock<HashMap<String, Arc<RdapResponse>>>>,
|
||||
pub(crate) srvhelps: Arc<RwLock<HashMap<String, Arc<RdapResponse>>>>,
|
||||
pub(crate) config: MemConfig,
|
||||
}
|
||||
|
||||
impl Mem {
|
||||
pub fn new(config: MemConfig) -> Self {
|
||||
Self {
|
||||
autnums: <_>::default(),
|
||||
ip4: <_>::default(),
|
||||
ip6: <_>::default(),
|
||||
domains: <_>::default(),
|
||||
domains_by_name: Arc::new(RwLock::new(SearchLabels::builder().build())),
|
||||
idns: <_>::default(),
|
||||
nameservers: <_>::default(),
|
||||
entities: <_>::default(),
|
||||
srvhelps: <_>::default(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mem {
|
||||
fn default() -> Self {
|
||||
Self::new(
|
||||
MemConfig::builder()
|
||||
.common_config(CommonConfig::default())
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl StoreOps for Mem {
|
||||
async fn init(&self) -> Result<(), RdapServerError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn new_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError> {
|
||||
Ok(Box::new(MemTx::new(self).await))
|
||||
}
|
||||
|
||||
async fn new_truncate_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError> {
|
||||
Ok(Box::new(MemTx::new_truncate(self)))
|
||||
}
|
||||
|
||||
async fn get_domain_by_ldh(&self, ldh: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
let domains = self.domains.read().await;
|
||||
let result = domains.get(ldh);
|
||||
match result {
|
||||
Some(domain) => Ok(RdapResponse::clone(domain)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_domain_by_unicode(&self, unicode: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
let idns = self.idns.read().await;
|
||||
let result = idns.get(unicode);
|
||||
match result {
|
||||
Some(domain) => Ok(RdapResponse::clone(domain)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_entity_by_handle(&self, handle: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
let entities = self.entities.read().await;
|
||||
let result = entities.get(handle);
|
||||
match result {
|
||||
Some(entity) => Ok(RdapResponse::clone(entity)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_nameserver_by_ldh(&self, ldh: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
let nameservers = self.nameservers.read().await;
|
||||
let result = nameservers.get(ldh);
|
||||
match result {
|
||||
Some(nameserver) => Ok(RdapResponse::clone(nameserver)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_autnum_by_num(&self, num: u32) -> Result<RdapResponse, RdapServerError> {
|
||||
let autnums = self.autnums.read().await;
|
||||
let result = autnums.get(num);
|
||||
match result {
|
||||
Some(autnum) => Ok(RdapResponse::clone(autnum)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_network_by_ipaddr(&self, ipaddr: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
let addr = ipaddr.parse::<IpAddr>()?;
|
||||
match addr {
|
||||
IpAddr::V4(v4) => {
|
||||
let slash32 = Ipv4Net::new(v4, 32)?;
|
||||
let ip4s = self.ip4.read().await;
|
||||
let result = ip4s.get_lpm(&slash32);
|
||||
match result {
|
||||
Some(network) => Ok(RdapResponse::clone(network.1)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
IpAddr::V6(v6) => {
|
||||
let slash128 = Ipv6Net::new(v6, 128)?;
|
||||
let ip6s = self.ip6.read().await;
|
||||
let result = ip6s.get_lpm(&slash128);
|
||||
match result {
|
||||
Some(network) => Ok(RdapResponse::clone(network.1)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_network_by_cidr(&self, cidr: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
let net = IpNet::from_str(cidr)?;
|
||||
match net {
|
||||
IpNet::V4(ipv4net) => {
|
||||
let ip4s = self.ip4.read().await;
|
||||
let result = ip4s.get_lpm(&ipv4net);
|
||||
match result {
|
||||
Some(network) => Ok(RdapResponse::clone(network.1)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
IpNet::V6(ipv6net) => {
|
||||
let ip6s = self.ip6.read().await;
|
||||
let result = ip6s.get_lpm(&ipv6net);
|
||||
match result {
|
||||
Some(network) => Ok(RdapResponse::clone(network.1)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_srv_help(&self, host: Option<&str>) -> Result<RdapResponse, RdapServerError> {
|
||||
let host = host.unwrap_or("..default");
|
||||
let srvhelps = self.srvhelps.read().await;
|
||||
let result = srvhelps.get(host);
|
||||
match result {
|
||||
Some(srvhelp) => Ok(RdapResponse::clone(srvhelp)),
|
||||
None => Ok(NOT_FOUND.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn search_domains_by_name(&self, name: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
if !self.config.common_config.domain_search_by_name_enable {
|
||||
return Ok(NOT_IMPLEMENTED.clone());
|
||||
}
|
||||
//else
|
||||
let domains_by_name = self.domains_by_name.read().await;
|
||||
let results = domains_by_name
|
||||
.search(name)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(Arc::<RdapResponse>::unwrap_or_clone)
|
||||
.filter_map(|d| match d {
|
||||
RdapResponse::Domain(d) => Some(*d),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<Domain>>();
|
||||
let response = DomainSearchResults::builder()
|
||||
.results(results)
|
||||
.build()
|
||||
.to_response();
|
||||
Ok(response)
|
||||
}
|
||||
}
|
330
icann-rdap-srv/src/storage/mem/tx.rs
Normal file
330
icann-rdap-srv/src/storage/mem/tx.rs
Normal file
|
@ -0,0 +1,330 @@
|
|||
use std::{collections::HashMap, net::IpAddr, str::FromStr, sync::Arc};
|
||||
|
||||
use {
|
||||
async_trait::async_trait,
|
||||
btree_range_map::RangeMap,
|
||||
icann_rdap_common::{
|
||||
prelude::ToResponse,
|
||||
response::{Autnum, Domain, Entity, Help, Nameserver, Network, RdapResponse, Rfc9083Error},
|
||||
},
|
||||
ipnet::{IpSubnets, Ipv4Net, Ipv4Subnets, Ipv6Net, Ipv6Subnets},
|
||||
prefix_trie::PrefixMap,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::RdapServerError,
|
||||
storage::{
|
||||
data::{AutnumId, DomainId, EntityId, NameserverId, NetworkId},
|
||||
TxHandle,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{label_search::SearchLabels, ops::Mem};
|
||||
|
||||
pub struct MemTx {
|
||||
mem: Mem,
|
||||
autnums: RangeMap<u32, Arc<RdapResponse>>,
|
||||
ip4: PrefixMap<Ipv4Net, Arc<RdapResponse>>,
|
||||
ip6: PrefixMap<Ipv6Net, Arc<RdapResponse>>,
|
||||
domains: HashMap<String, Arc<RdapResponse>>,
|
||||
domains_by_name: SearchLabels<Arc<RdapResponse>>,
|
||||
idns: HashMap<String, Arc<RdapResponse>>,
|
||||
nameservers: HashMap<String, Arc<RdapResponse>>,
|
||||
entities: HashMap<String, Arc<RdapResponse>>,
|
||||
srvhelps: HashMap<String, Arc<RdapResponse>>,
|
||||
}
|
||||
|
||||
impl MemTx {
|
||||
pub async fn new(mem: &Mem) -> Self {
|
||||
let domains = Arc::clone(&mem.domains).read_owned().await.clone();
|
||||
let mut domains_by_name = SearchLabels::builder().build();
|
||||
|
||||
// only do load up domain search labels if search by domain names is supported
|
||||
if mem.config.common_config.domain_search_by_name_enable {
|
||||
for (name, value) in domains.iter() {
|
||||
domains_by_name.insert(name, value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
mem: mem.clone(),
|
||||
autnums: Arc::clone(&mem.autnums).read_owned().await.clone(),
|
||||
ip4: Arc::clone(&mem.ip4).read_owned().await.clone(),
|
||||
ip6: Arc::clone(&mem.ip6).read_owned().await.clone(),
|
||||
domains,
|
||||
domains_by_name,
|
||||
idns: Arc::clone(&mem.idns).read_owned().await.clone(),
|
||||
nameservers: Arc::clone(&mem.nameservers).read_owned().await.clone(),
|
||||
entities: Arc::clone(&mem.entities).read_owned().await.clone(),
|
||||
srvhelps: Arc::clone(&mem.srvhelps).read_owned().await.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_truncate(mem: &Mem) -> Self {
|
||||
Self {
|
||||
mem: mem.clone(),
|
||||
autnums: RangeMap::new(),
|
||||
ip4: PrefixMap::new(),
|
||||
ip6: PrefixMap::new(),
|
||||
domains: HashMap::new(),
|
||||
domains_by_name: SearchLabels::builder().build(),
|
||||
idns: HashMap::new(),
|
||||
nameservers: HashMap::new(),
|
||||
entities: HashMap::new(),
|
||||
srvhelps: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TxHandle for MemTx {
|
||||
async fn add_entity(&mut self, entity: &Entity) -> Result<(), RdapServerError> {
|
||||
let handle = entity
|
||||
.object_common
|
||||
.handle
|
||||
.as_ref()
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("handle".to_string()))?;
|
||||
self.entities
|
||||
.insert(handle.to_owned(), Arc::new(entity.clone().to_response()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_entity_err(
|
||||
&mut self,
|
||||
entity_id: &EntityId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
self.entities.insert(
|
||||
entity_id.handle.to_owned(),
|
||||
Arc::new(error.clone().to_response()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_domain(&mut self, domain: &Domain) -> Result<(), RdapServerError> {
|
||||
let domain_response = Arc::new(domain.clone().to_response());
|
||||
|
||||
// add the domain as LDH, which is required.
|
||||
let ldh_name = domain
|
||||
.ldh_name
|
||||
.as_ref()
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("ldhName".to_string()))?;
|
||||
self.domains
|
||||
.insert(ldh_name.to_owned(), domain_response.clone());
|
||||
|
||||
// add the domain by unicodeName
|
||||
if let Some(unicode_name) = domain.unicode_name.as_ref() {
|
||||
self.idns
|
||||
.insert(unicode_name.to_owned(), domain_response.clone());
|
||||
};
|
||||
|
||||
if self.mem.config.common_config.domain_search_by_name_enable {
|
||||
self.domains_by_name.insert(ldh_name, domain_response);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_domain_err(
|
||||
&mut self,
|
||||
domain_id: &DomainId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
self.domains.insert(
|
||||
domain_id.ldh_name.to_owned(),
|
||||
Arc::new(error.clone().to_response()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_nameserver(&mut self, nameserver: &Nameserver) -> Result<(), RdapServerError> {
|
||||
let ldh_name = nameserver
|
||||
.ldh_name
|
||||
.as_ref()
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("ldhName".to_string()))?;
|
||||
self.nameservers.insert(
|
||||
ldh_name.to_owned(),
|
||||
Arc::new(nameserver.clone().to_response()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_nameserver_err(
|
||||
&mut self,
|
||||
nameserver_id: &NameserverId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
self.nameservers.insert(
|
||||
nameserver_id.ldh_name.to_owned(),
|
||||
Arc::new(error.clone().to_response()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_autnum(&mut self, autnum: &Autnum) -> Result<(), RdapServerError> {
|
||||
let start_num = autnum
|
||||
.start_autnum
|
||||
.as_ref()
|
||||
.and_then(|n| n.as_u32())
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("startNum".to_string()))?;
|
||||
let end_num = autnum
|
||||
.end_autnum
|
||||
.as_ref()
|
||||
.and_then(|n| n.as_u32())
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("endNum".to_string()))?;
|
||||
self.autnums.insert(
|
||||
(start_num)..=(end_num),
|
||||
Arc::new(autnum.clone().to_response()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_autnum_err(
|
||||
&mut self,
|
||||
autnum_id: &AutnumId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
self.autnums.insert(
|
||||
(autnum_id.start_autnum)..=(autnum_id.end_autnum),
|
||||
Arc::new(error.clone().to_response()),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_network(&mut self, network: &Network) -> Result<(), RdapServerError> {
|
||||
let start_addr = network
|
||||
.start_address
|
||||
.as_ref()
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("startAddress".to_string()))?;
|
||||
let end_addr = network
|
||||
.end_address
|
||||
.as_ref()
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("endAddress".to_string()))?;
|
||||
let ip_type = network
|
||||
.ip_version
|
||||
.as_ref()
|
||||
.ok_or_else(|| RdapServerError::EmptyIndexData("ipVersion".to_string()))?;
|
||||
let is_v4 = ip_type.eq_ignore_ascii_case("v4");
|
||||
if is_v4 {
|
||||
let subnets = Ipv4Subnets::new(start_addr.parse()?, end_addr.parse()?, 0);
|
||||
for net in subnets {
|
||||
self.ip4
|
||||
.insert(net, Arc::new(network.clone().to_response()));
|
||||
}
|
||||
} else {
|
||||
let subnets = Ipv6Subnets::new(start_addr.parse()?, end_addr.parse()?, 0);
|
||||
for net in subnets {
|
||||
self.ip6
|
||||
.insert(net, Arc::new(network.clone().to_response()));
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_network_err(
|
||||
&mut self,
|
||||
network_id: &NetworkId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
let subnets = match &network_id.network_id {
|
||||
crate::storage::data::NetworkIdType::Cidr(cidr) => cidr.subnets(cidr.prefix_len())?,
|
||||
crate::storage::data::NetworkIdType::Range {
|
||||
start_address,
|
||||
end_address,
|
||||
} => {
|
||||
let start_addr = IpAddr::from_str(start_address)?;
|
||||
let end_addr = IpAddr::from_str(end_address)?;
|
||||
if start_addr.is_ipv4() && end_addr.is_ipv4() {
|
||||
let IpAddr::V4(start_addr) = start_addr else {
|
||||
panic!("check failed")
|
||||
};
|
||||
let IpAddr::V4(end_addr) = end_addr else {
|
||||
panic!("check failed")
|
||||
};
|
||||
IpSubnets::from(Ipv4Subnets::new(start_addr, end_addr, 0))
|
||||
} else if start_addr.is_ipv6() && end_addr.is_ipv6() {
|
||||
let IpAddr::V6(start_addr) = start_addr else {
|
||||
panic!("check failed")
|
||||
};
|
||||
let IpAddr::V6(end_addr) = end_addr else {
|
||||
panic!("check failed")
|
||||
};
|
||||
IpSubnets::from(Ipv6Subnets::new(start_addr, end_addr, 0))
|
||||
} else {
|
||||
return Err(RdapServerError::EmptyIndexData(
|
||||
"mismatch ip version".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
match subnets {
|
||||
IpSubnets::V4(subnets) => {
|
||||
for net in subnets {
|
||||
self.ip4.insert(net, Arc::new(error.clone().to_response()));
|
||||
}
|
||||
}
|
||||
IpSubnets::V6(subnets) => {
|
||||
for net in subnets {
|
||||
self.ip6.insert(net, Arc::new(error.clone().to_response()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_srv_help(
|
||||
&mut self,
|
||||
help: &Help,
|
||||
host: Option<&str>,
|
||||
) -> Result<(), RdapServerError> {
|
||||
let host = host.unwrap_or("..default");
|
||||
self.srvhelps
|
||||
.insert(host.to_string(), Arc::new(help.clone().to_response()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn commit(mut self: Box<Self>) -> Result<(), RdapServerError> {
|
||||
// autnums
|
||||
let mut autnum_g = self.mem.autnums.write().await;
|
||||
std::mem::swap(&mut self.autnums, &mut autnum_g);
|
||||
|
||||
// ip4
|
||||
let mut ip4_g = self.mem.ip4.write().await;
|
||||
std::mem::swap(&mut self.ip4, &mut ip4_g);
|
||||
|
||||
// ip6
|
||||
let mut ip6_g = self.mem.ip6.write().await;
|
||||
std::mem::swap(&mut self.ip6, &mut ip6_g);
|
||||
|
||||
// domains
|
||||
let mut domains_g = self.mem.domains.write().await;
|
||||
std::mem::swap(&mut self.domains, &mut domains_g);
|
||||
|
||||
//domains by name
|
||||
let mut domains_by_name_g = self.mem.domains_by_name.write().await;
|
||||
std::mem::swap(&mut self.domains_by_name, &mut domains_by_name_g);
|
||||
|
||||
//idns
|
||||
let mut idns_g = self.mem.idns.write().await;
|
||||
std::mem::swap(&mut self.idns, &mut idns_g);
|
||||
|
||||
// nameservers
|
||||
let mut nameservers_g = self.mem.nameservers.write().await;
|
||||
std::mem::swap(&mut self.nameservers, &mut nameservers_g);
|
||||
|
||||
// entities
|
||||
let mut entities_g = self.mem.entities.write().await;
|
||||
std::mem::swap(&mut self.entities, &mut entities_g);
|
||||
|
||||
//srvhelps
|
||||
let mut srvhelps_g = self.mem.srvhelps.write().await;
|
||||
std::mem::swap(&mut self.srvhelps, &mut srvhelps_g);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn rollback(self: Box<Self>) -> Result<(), RdapServerError> {
|
||||
// Nothing to do.
|
||||
Ok(())
|
||||
}
|
||||
}
|
142
icann-rdap-srv/src/storage/mod.rs
Normal file
142
icann-rdap-srv/src/storage/mod.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
use {
|
||||
async_trait::async_trait,
|
||||
buildstructor::Builder,
|
||||
icann_rdap_common::response::{
|
||||
Autnum, Domain, Entity, Help, Nameserver, Network, RdapResponse, Rfc9083Error,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::error::RdapServerError;
|
||||
|
||||
use self::data::{AutnumId, DomainId, EntityId, NameserverId, NetworkId};
|
||||
|
||||
pub mod data;
|
||||
pub mod mem;
|
||||
pub mod pg;
|
||||
|
||||
pub type DynStoreOps = dyn StoreOps + Send + Sync;
|
||||
|
||||
/// This trait defines the operations for a storage engine.
|
||||
#[async_trait]
|
||||
pub trait StoreOps: Send + Sync {
|
||||
/// Initializes the backend storage
|
||||
async fn init(&self) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Gets a new transaction.
|
||||
async fn new_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError>;
|
||||
|
||||
/// Gets a new transaction in which all the previous data has been truncated (cleared).
|
||||
async fn new_truncate_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError>;
|
||||
|
||||
/// Get a domain from storage using the 'ldhName' as the key.
|
||||
async fn get_domain_by_ldh(&self, ldh: &str) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Get a domain from storage using the 'unicodeName' as the key.
|
||||
async fn get_domain_by_unicode(&self, unicode: &str) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Get an entity from storage using the 'handle' of the entity as the key.
|
||||
async fn get_entity_by_handle(&self, handle: &str) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Get a nameserver from storage using the 'ldhName' as the key.
|
||||
async fn get_nameserver_by_ldh(&self, ldh: &str) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Get an autnum from storage using an autonomous system numbers as the key.
|
||||
async fn get_autnum_by_num(&self, num: u32) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Get a network from storage using an IP address. The network returned should be the
|
||||
/// most specific (longest prefix) network containing the IP address.
|
||||
async fn get_network_by_ipaddr(&self, ipaddr: &str) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Get a network from storage using a CIDR notation network (e.g. "10.0.0.0/8"). The IP address
|
||||
/// portion of the CIDR should be assumed to be complete, that is not "10.0/8". The network
|
||||
/// returned should be the most specific (longest prefix) network containing the IP address.
|
||||
async fn get_network_by_cidr(&self, cidr: &str) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Get server help.
|
||||
async fn get_srv_help(&self, host: Option<&str>) -> Result<RdapResponse, RdapServerError>;
|
||||
|
||||
/// Search for domains by name.
|
||||
async fn search_domains_by_name(&self, name: &str) -> Result<RdapResponse, RdapServerError>;
|
||||
}
|
||||
|
||||
/// Represents a handle to a transaction.
|
||||
/// The implementation of the transaction
|
||||
/// are dependent on the storage type.
|
||||
#[async_trait]
|
||||
pub trait TxHandle: Send {
|
||||
/// Add a domain name to storage.
|
||||
async fn add_domain(&mut self, domain: &Domain) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add an error as a domain to storage. This is useful for specifying redirects.
|
||||
async fn add_domain_err(
|
||||
&mut self,
|
||||
domain_id: &DomainId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add an entitty to storage.
|
||||
async fn add_entity(&mut self, entity: &Entity) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add an error as an entity to storage. This is useful for specifying redirects.
|
||||
async fn add_entity_err(
|
||||
&mut self,
|
||||
entity_id: &EntityId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add a nameserver to storage.
|
||||
async fn add_nameserver(&mut self, nameserver: &Nameserver) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add an error as a nameserver to storage. This is useful for specifying redirects.
|
||||
async fn add_nameserver_err(
|
||||
&mut self,
|
||||
nameserver_id: &NameserverId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add a nameserver to storage.
|
||||
async fn add_autnum(&mut self, autnum: &Autnum) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add an error as an autnum to storage. This is useful for specifying redirects.
|
||||
async fn add_autnum_err(
|
||||
&mut self,
|
||||
autnum_id: &AutnumId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add a network to storage.
|
||||
async fn add_network(&mut self, network: &Network) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Add a network as an autnum to storage. This is useful for specifying redirects.
|
||||
async fn add_network_err(
|
||||
&mut self,
|
||||
network_id: &NetworkId,
|
||||
error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError>;
|
||||
|
||||
async fn add_srv_help(
|
||||
&mut self,
|
||||
help: &Help,
|
||||
host: Option<&str>,
|
||||
) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Commit the transaction.
|
||||
async fn commit(self: Box<Self>) -> Result<(), RdapServerError>;
|
||||
|
||||
/// Rollback the transaction.
|
||||
async fn rollback(self: Box<Self>) -> Result<(), RdapServerError>;
|
||||
}
|
||||
|
||||
/// Common configuration for storage back ends.
|
||||
#[derive(Debug, Clone, Copy, Builder)]
|
||||
pub struct CommonConfig {
|
||||
pub domain_search_by_name_enable: bool,
|
||||
}
|
||||
|
||||
impl Default for CommonConfig {
|
||||
fn default() -> Self {
|
||||
CommonConfig {
|
||||
domain_search_by_name_enable: true,
|
||||
}
|
||||
}
|
||||
}
|
9
icann-rdap-srv/src/storage/pg/config.rs
Normal file
9
icann-rdap-srv/src/storage/pg/config.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use buildstructor::Builder;
|
||||
|
||||
use crate::storage::CommonConfig;
|
||||
|
||||
#[derive(Debug, Builder, Clone)]
|
||||
pub struct PgConfig {
|
||||
pub db_url: String,
|
||||
pub common_config: CommonConfig,
|
||||
}
|
5
icann-rdap-srv/src/storage/pg/mod.rs
Normal file
5
icann-rdap-srv/src/storage/pg/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
#![allow(dead_code)] // TODO remove
|
||||
|
||||
pub mod config;
|
||||
pub mod ops;
|
||||
pub mod tx;
|
78
icann-rdap-srv/src/storage/pg/ops.rs
Normal file
78
icann-rdap-srv/src/storage/pg/ops.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
#![allow(clippy::diverging_sub_expression)]
|
||||
use {
|
||||
async_trait::async_trait,
|
||||
icann_rdap_common::response::RdapResponse,
|
||||
sqlx::{query, PgPool},
|
||||
tracing::{debug, info},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::RdapServerError,
|
||||
storage::{StoreOps, TxHandle},
|
||||
};
|
||||
|
||||
use super::{config::PgConfig, tx::PgTx};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Pg {
|
||||
pg_pool: PgPool,
|
||||
}
|
||||
|
||||
impl Pg {
|
||||
pub async fn new(config: PgConfig) -> Result<Self, RdapServerError> {
|
||||
let pg_pool = PgPool::connect(&config.db_url).await?;
|
||||
Ok(Self { pg_pool })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl StoreOps for Pg {
|
||||
async fn init(&self) -> Result<(), RdapServerError> {
|
||||
debug!("Testing database connection.");
|
||||
let mut conn = self.pg_pool.acquire().await?;
|
||||
query("select 1").fetch_one(&mut *conn).await?;
|
||||
info!("Database connection test is successful.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn new_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError> {
|
||||
Ok(Box::new(PgTx::new(&self.pg_pool).await?))
|
||||
}
|
||||
|
||||
async fn new_truncate_tx(&self) -> Result<Box<dyn TxHandle>, RdapServerError> {
|
||||
Ok(Box::new(PgTx::new_truncate(&self.pg_pool).await?))
|
||||
}
|
||||
|
||||
async fn get_domain_by_ldh(&self, _ldh: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_domain_by_unicode(&self, _unicode: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_entity_by_handle(&self, _handle: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_nameserver_by_ldh(&self, _ldh: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_autnum_by_num(&self, _num: u32) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_network_by_ipaddr(&self, _ipaddr: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
async fn get_network_by_cidr(&self, _cidr: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
async fn get_srv_help(&self, _host: Option<&str>) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
async fn search_domains_by_name(&self, _name: &str) -> Result<RdapResponse, RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
119
icann-rdap-srv/src/storage/pg/tx.rs
Normal file
119
icann-rdap-srv/src/storage/pg/tx.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
#![allow(clippy::diverging_sub_expression)]
|
||||
use {
|
||||
async_trait::async_trait,
|
||||
icann_rdap_common::response::{Autnum, Domain, Entity, Nameserver, Network, Rfc9083Error},
|
||||
sqlx::{PgPool, Postgres},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::RdapServerError,
|
||||
storage::{
|
||||
data::{AutnumId, DomainId, EntityId, NameserverId, NetworkId},
|
||||
TxHandle,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct PgTx<'a> {
|
||||
db_tx: sqlx::Transaction<'a, Postgres>,
|
||||
}
|
||||
|
||||
impl<'a> PgTx<'a> {
|
||||
pub async fn new(pg_pool: &PgPool) -> Result<Self, RdapServerError> {
|
||||
let db_tx = pg_pool.begin().await?;
|
||||
Ok(Self { db_tx })
|
||||
}
|
||||
|
||||
pub async fn new_truncate(pg_pool: &PgPool) -> Result<Self, RdapServerError> {
|
||||
let mut db_tx = pg_pool.begin().await?;
|
||||
// TODO actually complete this
|
||||
// this is just here to make sure something will compile
|
||||
sqlx::query("truncate domain").execute(&mut *db_tx).await?;
|
||||
Ok(Self { db_tx })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TxHandle for PgTx<'_> {
|
||||
async fn add_entity(&mut self, _entity: &Entity) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_entity_err(
|
||||
&mut self,
|
||||
_entity_id: &EntityId,
|
||||
_error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_domain(&mut self, _domain: &Domain) -> Result<(), RdapServerError> {
|
||||
// TODO actually complete this
|
||||
// this is just here to make sure something will compile
|
||||
sqlx::query("insert domain")
|
||||
.execute(&mut *self.db_tx)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_domain_err(
|
||||
&mut self,
|
||||
_domain_id: &DomainId,
|
||||
_error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_nameserver(&mut self, _nameserver: &Nameserver) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_nameserver_err(
|
||||
&mut self,
|
||||
_nameserver_id: &NameserverId,
|
||||
_error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_autnum(&mut self, _autnum: &Autnum) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_autnum_err(
|
||||
&mut self,
|
||||
_autnum_id: &AutnumId,
|
||||
_error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_network(&mut self, _network: &Network) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_network_err(
|
||||
&mut self,
|
||||
_network_id: &NetworkId,
|
||||
_error: &Rfc9083Error,
|
||||
) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn add_srv_help(
|
||||
&mut self,
|
||||
_help: &icann_rdap_common::response::Help,
|
||||
_host: Option<&str>,
|
||||
) -> Result<(), RdapServerError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn commit(self: Box<Self>) -> Result<(), RdapServerError> {
|
||||
self.db_tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn rollback(self: Box<Self>) -> Result<(), RdapServerError> {
|
||||
self.db_tx.rollback().await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue