|
@ -1,9 +1,9 @@ |
|
|
use crate::acme_proto::structs::{AcmeError, ApiError, Directory, HttpApiError};
|
|
|
use crate::acme_proto::structs::{AcmeError, ApiError, Directory, HttpApiError};
|
|
|
|
|
|
use crate::certificate::Certificate;
|
|
|
use acme_common::error::Error;
|
|
|
use acme_common::error::Error;
|
|
|
use http_req::request::{Method, Request};
|
|
|
use http_req::request::{Method, Request};
|
|
|
use http_req::response::Response;
|
|
|
use http_req::response::Response;
|
|
|
use http_req::uri::Uri;
|
|
|
use http_req::uri::Uri;
|
|
|
use log::{debug, trace, warn};
|
|
|
|
|
|
use std::path::Path;
|
|
|
use std::path::Path;
|
|
|
use std::str::FromStr;
|
|
|
use std::str::FromStr;
|
|
|
use std::{thread, time};
|
|
|
use std::{thread, time};
|
|
@ -25,8 +25,13 @@ impl FromStr for DummyString { |
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn new_request<'a>(root_certs: &'a [String], uri: &'a Uri, method: Method) -> Request<'a> {
|
|
|
|
|
|
debug!("{}: {}", method, uri);
|
|
|
|
|
|
|
|
|
fn new_request<'a>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
|
|
|
root_certs: &'a [String],
|
|
|
|
|
|
uri: &'a Uri,
|
|
|
|
|
|
method: Method,
|
|
|
|
|
|
) -> Request<'a> {
|
|
|
|
|
|
cert.debug(&format!("{}: {}", method, uri));
|
|
|
let useragent = format!(
|
|
|
let useragent = format!(
|
|
|
"{}/{} ({}) {}",
|
|
|
"{}/{} ({}) {}",
|
|
|
crate::APP_NAME,
|
|
|
crate::APP_NAME,
|
|
@ -52,7 +57,7 @@ fn send_request(request: &Request) -> Result<(Response, String), Error> { |
|
|
Ok((res, res_str))
|
|
|
Ok((res, res_str))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn send_request_retry(request: &Request) -> Result<(Response, String), Error> {
|
|
|
|
|
|
|
|
|
fn send_request_retry(cert: &Certificate, request: &Request) -> Result<(Response, String), Error> {
|
|
|
for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY {
|
|
|
for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY {
|
|
|
let (res, res_body) = send_request(request)?;
|
|
|
let (res, res_body) = send_request(request)?;
|
|
|
match check_response(&res, &res_body) {
|
|
|
match check_response(&res, &res_body) {
|
|
@ -64,7 +69,7 @@ fn send_request_retry(request: &Request) -> Result<(Response, String), Error> { |
|
|
let msg = format!("HTTP error: {}: {}", res.status_code(), res.reason());
|
|
|
let msg = format!("HTTP error: {}: {}", res.status_code(), res.reason());
|
|
|
return Err(msg.into());
|
|
|
return Err(msg.into());
|
|
|
}
|
|
|
}
|
|
|
warn!("{}", e);
|
|
|
|
|
|
|
|
|
cert.warn(&format!("{}", e));
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
};
|
|
|
thread::sleep(time::Duration::from_secs(crate::DEFAULT_HTTP_FAIL_WAIT_SEC));
|
|
|
thread::sleep(time::Duration::from_secs(crate::DEFAULT_HTTP_FAIL_WAIT_SEC));
|
|
@ -86,10 +91,10 @@ fn is_nonce(data: &str) -> bool { |
|
|
.all(|c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
|
|
.all(|c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn nonce_from_response(res: &Response) -> Result<String, Error> {
|
|
|
|
|
|
|
|
|
fn nonce_from_response(cert: &Certificate, res: &Response) -> Result<String, Error> {
|
|
|
let nonce = get_header(res, "Replay-Nonce")?;
|
|
|
let nonce = get_header(res, "Replay-Nonce")?;
|
|
|
if is_nonce(&nonce) {
|
|
|
if is_nonce(&nonce) {
|
|
|
trace!("New nonce: {}", nonce);
|
|
|
|
|
|
|
|
|
cert.trace(&format!("New nonce: {}", nonce));
|
|
|
Ok(nonce.to_string())
|
|
|
Ok(nonce.to_string())
|
|
|
} else {
|
|
|
} else {
|
|
|
let msg = format!("{}: invalid nonce.", nonce);
|
|
|
let msg = format!("{}: invalid nonce.", nonce);
|
|
@ -98,21 +103,22 @@ fn nonce_from_response(res: &Response) -> Result<String, Error> { |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn post_jose_type(
|
|
|
fn post_jose_type(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data: &[u8],
|
|
|
data: &[u8],
|
|
|
accept_type: &str,
|
|
|
accept_type: &str,
|
|
|
) -> Result<(Response, String), Error> {
|
|
|
) -> Result<(Response, String), Error> {
|
|
|
let uri = url.parse::<Uri>()?;
|
|
|
let uri = url.parse::<Uri>()?;
|
|
|
let mut request = new_request(root_certs, &uri, Method::POST);
|
|
|
|
|
|
|
|
|
let mut request = new_request(cert, root_certs, &uri, Method::POST);
|
|
|
request.header("Content-Type", CONTENT_TYPE_JOSE);
|
|
|
request.header("Content-Type", CONTENT_TYPE_JOSE);
|
|
|
request.header("Content-Length", &data.len().to_string());
|
|
|
request.header("Content-Length", &data.len().to_string());
|
|
|
request.header("Accept", accept_type);
|
|
|
request.header("Accept", accept_type);
|
|
|
request.body(data);
|
|
|
request.body(data);
|
|
|
let rstr = String::from_utf8_lossy(data);
|
|
|
let rstr = String::from_utf8_lossy(data);
|
|
|
trace!("request body: {}", rstr);
|
|
|
|
|
|
|
|
|
cert.trace(&format!("request body: {}", rstr));
|
|
|
let (res, res_body) = send_request(&request)?;
|
|
|
let (res, res_body) = send_request(&request)?;
|
|
|
trace!("response body: {}", res_body);
|
|
|
|
|
|
|
|
|
cert.trace(&format!("response body: {}", res_body));
|
|
|
Ok((res, res_body))
|
|
|
Ok((res, res_body))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
@ -125,6 +131,7 @@ fn check_response(res: &Response, body: &str) -> Result<(), AcmeError> { |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn fetch_obj_type<T, G>(
|
|
|
fn fetch_obj_type<T, G>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data_builder: &G,
|
|
|
data_builder: &G,
|
|
@ -138,8 +145,8 @@ where |
|
|
let mut nonce = nonce.to_string();
|
|
|
let mut nonce = nonce.to_string();
|
|
|
for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY {
|
|
|
for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY {
|
|
|
let data = data_builder(&nonce)?;
|
|
|
let data = data_builder(&nonce)?;
|
|
|
let (res, res_body) = post_jose_type(root_certs, url, data.as_bytes(), accept_type)?;
|
|
|
|
|
|
nonce = nonce_from_response(&res)?;
|
|
|
|
|
|
|
|
|
let (res, res_body) = post_jose_type(cert, root_certs, url, data.as_bytes(), accept_type)?;
|
|
|
|
|
|
nonce = nonce_from_response(cert, &res)?;
|
|
|
|
|
|
|
|
|
match check_response(&res, &res_body) {
|
|
|
match check_response(&res, &res_body) {
|
|
|
Ok(()) => {
|
|
|
Ok(()) => {
|
|
@ -152,7 +159,7 @@ where |
|
|
let msg = format!("HTTP error: {}: {}", res.status_code(), res.reason());
|
|
|
let msg = format!("HTTP error: {}: {}", res.status_code(), res.reason());
|
|
|
return Err(msg.into());
|
|
|
return Err(msg.into());
|
|
|
}
|
|
|
}
|
|
|
warn!("{}", e);
|
|
|
|
|
|
|
|
|
cert.warn(&format!("{}", e));
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
};
|
|
|
thread::sleep(time::Duration::from_secs(crate::DEFAULT_HTTP_FAIL_WAIT_SEC));
|
|
|
thread::sleep(time::Duration::from_secs(crate::DEFAULT_HTTP_FAIL_WAIT_SEC));
|
|
@ -161,6 +168,7 @@ where |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn fetch_obj<T, G>(
|
|
|
fn fetch_obj<T, G>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data_builder: &G,
|
|
|
data_builder: &G,
|
|
@ -170,10 +178,18 @@ where |
|
|
T: std::str::FromStr<Err = Error>,
|
|
|
T: std::str::FromStr<Err = Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
{
|
|
|
{
|
|
|
fetch_obj_type(root_certs, url, data_builder, nonce, CONTENT_TYPE_JSON)
|
|
|
|
|
|
|
|
|
fetch_obj_type(
|
|
|
|
|
|
cert,
|
|
|
|
|
|
root_certs,
|
|
|
|
|
|
url,
|
|
|
|
|
|
data_builder,
|
|
|
|
|
|
nonce,
|
|
|
|
|
|
CONTENT_TYPE_JSON,
|
|
|
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn get_obj_loc<T, G>(
|
|
|
pub fn get_obj_loc<T, G>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data_builder: &G,
|
|
|
data_builder: &G,
|
|
@ -183,7 +199,7 @@ where |
|
|
T: std::str::FromStr<Err = Error>,
|
|
|
T: std::str::FromStr<Err = Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
{
|
|
|
{
|
|
|
let (obj, location, nonce) = fetch_obj(root_certs, url, data_builder, nonce)?;
|
|
|
|
|
|
|
|
|
let (obj, location, nonce) = fetch_obj(cert, root_certs, url, data_builder, nonce)?;
|
|
|
if location.is_empty() {
|
|
|
if location.is_empty() {
|
|
|
Err("Location header not found.".into())
|
|
|
Err("Location header not found.".into())
|
|
|
} else {
|
|
|
} else {
|
|
@ -192,6 +208,7 @@ where |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn get_obj<T, G>(
|
|
|
pub fn get_obj<T, G>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data_builder: &G,
|
|
|
data_builder: &G,
|
|
@ -201,11 +218,12 @@ where |
|
|
T: std::str::FromStr<Err = Error>,
|
|
|
T: std::str::FromStr<Err = Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
{
|
|
|
{
|
|
|
let (obj, _, nonce) = fetch_obj(root_certs, url, data_builder, nonce)?;
|
|
|
|
|
|
|
|
|
let (obj, _, nonce) = fetch_obj(cert, root_certs, url, data_builder, nonce)?;
|
|
|
Ok((obj, nonce))
|
|
|
Ok((obj, nonce))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn pool_obj<T, G, S>(
|
|
|
pub fn pool_obj<T, G, S>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data_builder: &G,
|
|
|
data_builder: &G,
|
|
@ -220,12 +238,12 @@ where |
|
|
let mut nonce: String = nonce.to_string();
|
|
|
let mut nonce: String = nonce.to_string();
|
|
|
for _ in 0..crate::DEFAULT_POOL_NB_TRIES {
|
|
|
for _ in 0..crate::DEFAULT_POOL_NB_TRIES {
|
|
|
thread::sleep(time::Duration::from_secs(crate::DEFAULT_POOL_WAIT_SEC));
|
|
|
thread::sleep(time::Duration::from_secs(crate::DEFAULT_POOL_WAIT_SEC));
|
|
|
let (obj, _, new_nonce) = fetch_obj(root_certs, url, data_builder, &nonce)?;
|
|
|
|
|
|
|
|
|
let (obj, _, new_nonce) = fetch_obj(cert, root_certs, url, data_builder, &nonce)?;
|
|
|
if break_fn(&obj) {
|
|
|
if break_fn(&obj) {
|
|
|
return Ok((obj, new_nonce));
|
|
|
return Ok((obj, new_nonce));
|
|
|
}
|
|
|
}
|
|
|
if let Some(e) = obj.get_error() {
|
|
|
if let Some(e) = obj.get_error() {
|
|
|
warn!("Error: {}", e);
|
|
|
|
|
|
|
|
|
cert.warn(&e.prefix("Error").message);
|
|
|
}
|
|
|
}
|
|
|
nonce = new_nonce;
|
|
|
nonce = new_nonce;
|
|
|
}
|
|
|
}
|
|
@ -234,6 +252,7 @@ where |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn post_challenge_response<G>(
|
|
|
pub fn post_challenge_response<G>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data_builder: &G,
|
|
|
data_builder: &G,
|
|
@ -243,11 +262,12 @@ where |
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
{
|
|
|
{
|
|
|
let (_, _, nonce): (DummyString, String, String) =
|
|
|
let (_, _, nonce): (DummyString, String, String) =
|
|
|
fetch_obj(root_certs, url, data_builder, nonce)?;
|
|
|
|
|
|
|
|
|
fetch_obj(cert, root_certs, url, data_builder, nonce)?;
|
|
|
Ok(nonce)
|
|
|
Ok(nonce)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn get_certificate<G>(
|
|
|
pub fn get_certificate<G>(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
root_certs: &[String],
|
|
|
root_certs: &[String],
|
|
|
url: &str,
|
|
|
url: &str,
|
|
|
data_builder: &G,
|
|
|
data_builder: &G,
|
|
@ -257,25 +277,29 @@ where |
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
G: Fn(&str) -> Result<String, Error>,
|
|
|
{
|
|
|
{
|
|
|
let (res_body, _, nonce): (DummyString, String, String) =
|
|
|
let (res_body, _, nonce): (DummyString, String, String) =
|
|
|
fetch_obj(root_certs, url, data_builder, nonce)?;
|
|
|
|
|
|
|
|
|
fetch_obj(cert, root_certs, url, data_builder, nonce)?;
|
|
|
Ok((res_body.content, nonce))
|
|
|
Ok((res_body.content, nonce))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn get_directory(root_certs: &[String], url: &str) -> Result<Directory, Error> {
|
|
|
|
|
|
|
|
|
pub fn get_directory(
|
|
|
|
|
|
cert: &Certificate,
|
|
|
|
|
|
root_certs: &[String],
|
|
|
|
|
|
url: &str,
|
|
|
|
|
|
) -> Result<Directory, Error> {
|
|
|
let uri = url.parse::<Uri>()?;
|
|
|
let uri = url.parse::<Uri>()?;
|
|
|
let mut request = new_request(root_certs, &uri, Method::GET);
|
|
|
|
|
|
|
|
|
let mut request = new_request(cert, root_certs, &uri, Method::GET);
|
|
|
request.header("Accept", CONTENT_TYPE_JSON);
|
|
|
request.header("Accept", CONTENT_TYPE_JSON);
|
|
|
let (r, s) = send_request_retry(&request)?;
|
|
|
|
|
|
|
|
|
let (r, s) = send_request_retry(cert, &request)?;
|
|
|
check_response(&r, &s)?;
|
|
|
check_response(&r, &s)?;
|
|
|
Directory::from_str(&s)
|
|
|
Directory::from_str(&s)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn get_nonce(root_certs: &[String], url: &str) -> Result<String, Error> {
|
|
|
|
|
|
|
|
|
pub fn get_nonce(cert: &Certificate, root_certs: &[String], url: &str) -> Result<String, Error> {
|
|
|
let uri = url.parse::<Uri>()?;
|
|
|
let uri = url.parse::<Uri>()?;
|
|
|
let request = new_request(root_certs, &uri, Method::HEAD);
|
|
|
|
|
|
let (res, res_body) = send_request_retry(&request)?;
|
|
|
|
|
|
|
|
|
let request = new_request(cert, root_certs, &uri, Method::HEAD);
|
|
|
|
|
|
let (res, res_body) = send_request_retry(cert, &request)?;
|
|
|
check_response(&res, &res_body)?;
|
|
|
check_response(&res, &res_body)?;
|
|
|
nonce_from_response(&res)
|
|
|
|
|
|
|
|
|
nonce_from_response(cert, &res)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
#[cfg(test)]
|
|
|