Lines
34.04 %
Functions
35.71 %
Branches
100 %
//! Helpers for capability declaration and negotiation.
// TODO #2598: Remove duplicate code between tor_hsclient::caps,
// tor_hsservice::caps, and tor_proto::circuit (to the extent possible).
use cfg_if::cfg_if;
use tor_cell::relaycell::hs::intro_payload::IntroduceHandshakePayload;
use tor_protover::Protocols;
use crate::IntroRequestError;
cfg_if! {
if #[cfg(feature = "negotiate-extensions")] {
use std::sync::LazyLock;
use tor_protover::{NamedSubver, named::*};
/// A set of protocols that we can advertise in our HS descriptor,
/// if we support them.
static ADVERTIZABLE_PROTOCOLS: LazyLock<Protocols> = LazyLock::new(
|| [RELAY_CRYPT_CGO, RELAY_NEGOTIATE_SUBPROTO].into_iter().collect()
);
/// An array of protocols that can be requested via a subprotocol
/// request extension.
///
/// Note that we don't necessarily support all of these ourselves!
static REQUESTABLE_LIST: &[NamedSubver] = &[
RELAY_CRYPT_CGO
];
/// A set of protocols that can be requested via a subprotocol request extension.
static REQUESTABLE: LazyLock<Protocols> = LazyLock::new(
|| REQUESTABLE_LIST.iter().copied().collect()
/// A set of protocols that we should advertise, if supported,
/// in our `flow-control` line.
static ADVERTISE_FLOWCTRL: LazyLock<Protocols> = LazyLock::new(
|| [FLOWCTRL_AUTH_SENDME, FLOWCTRL_CC].into_iter().collect()
}
/// Return the list of protocols that we should advertise in our hsdesc.
pub(crate) fn declared_protocols() -> Protocols {
tor_proto::supported_client_protocols().intersection(&ADVERTIZABLE_PROTOCOLS)
} else {
Protocols::new()
/// Return the flow control information that we should include in our hsdesc.
pub(crate) fn declared_flowctrl(
params: &tor_netdir::params::NetParameters,
) -> Option<(Protocols, u8)> {
let supported = tor_proto::supported_client_protocols();
supported.supports_named_subver(FLOWCTRL_CC).then(|| {
(
supported.intersection(&ADVERTISE_FLOWCTRL),
params.cc_sendme_inc.into()
)
})
None
/// Return a set of capabilities requested by the client in an INTRODUCE2 message payload.
/// The returned set will include capabilities that are...
/// - Requested explicitly by using `SUBPROTOCOL_REQUEST`
/// - Requested implicitly by using `FLOWCTRL_CC`
/// Returns an error if the client requested any capability that we do not support,
/// or which may not be included in a list of requested protocols.
#[cfg(feature = "negotiate-extensions")]
pub(crate) fn negotiated_capabilities(
intro: &IntroduceHandshakePayload,
) -> Result<Protocols, IntroRequestError> {
let mut requested = Vec::new();
// A list of capabilities (at the tor_proto level!) that we actually support.
//
// (We could possibly stop checking this once the flowctrl-cc and counter-galois-onion features
// are always-on.)
// We use this instead of annotating NEGOTIABLE_REQUEST_LIST members with `cfg`,
// because we don't want to add our own set of flowctrl-cc/counter-galois-onion features
// to match tor-proto.
let supported_protocols = tor_proto::supported_client_protocols();
if intro.cc_request_extension().is_some() {
// The client asked for FLOWCTRL_CC. If we support it, add it to `requested`.
// Otherwise, reject the request.
if supported_protocols.supports_named_subver(FLOWCTRL_CC) {
requested.push(FLOWCTRL_CC);
return Err(IntroRequestError::UnsupportedCapability);
if let Some(sp) = intro.subprotocol_request_extension() {
if !sp.contains_only(&REQUESTABLE) {
// Some capability was listed that is not supported at all with this extension.
// Reject.
for cap in REQUESTABLE_LIST {
if sp.contains(*cap) {
if !supported_protocols.supports_named_subver(*cap) {
// They requested something that we don't support. Reject the request.
requested.push(*cap);
let requested: Protocols = requested.into_iter().collect();
// We cannot provide CGO unless we also have negotiated CC .
if requested.supports_named_subver(RELAY_CRYPT_CGO)
&& !requested.supports_named_subver(FLOWCTRL_CC)
{
Ok(requested)
/// Legacy implementation of negotiated_capabilities.
// TODO: Remove this, and make the entire negotiate-extensions feature always-on.
#[cfg(not(feature = "negotiate-extensions"))]
// In this case, we "do not recognize" SUBPROTO_REQUEST,
// and so we ignore it.
Ok(Protocols::new())