1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Functionality for negotiating protocol capabilities.
// 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_netdoc::doc::hsdesc::HsDesc;
use tor_protover::Protocols;
cfg_if! {
if #[cfg(feature = "negotiate-extensions")] {
use tor_protover::{NamedSubver, NumberedSubver, named::*};
/// 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 peer capabilities, derived from a service's hsdesc.
#[derive(Clone, Debug)]
pub(crate) struct PeerCaps {
/// A list of the protocol capabilities that we share with the service,
/// and are able to negotiate with it. This includes flowctrl,
/// which is negotiated with a different extension than the others.
#[cfg(feature = "negotiate-extensions")]
shared_protos: Protocols,
/// A list of the protocols that we intend to request from the service
/// via the SUBPROTOCOL_REQUEST extension.
#[cfg(feature = "negotiate-extensions")]
request_protos: Protocols,
/// Our chosen cc_sendme_inc value to share with the service.
///
/// This is present if and only if `shared_protos` contains `FLOWCTRL_CC`
#[cfg(feature = "negotiate-extensions")]
sendme_inc: Option<u8>,
}
#[cfg(feature = "negotiate-extensions")]
impl PeerCaps {
/// Construct a new PeerCaps for a peer, given its descriptor.
pub(crate) fn new(desc: &HsDesc) -> Self {
let supported = tor_proto::supported_client_protocols();
let (fc_protos, sendme_inc) = if let Some((fcp, inc)) = desc.flow_control()
&& fcp.supports_named_subver(FLOWCTRL_CC)
&& supported.supports_named_subver(FLOWCTRL_CC)
{
([FLOWCTRL_CC].into_iter().collect(), Some(inc.into()))
} else {
(Protocols::new(), None)
};
let mut subproto_request = Vec::new();
let mutual_protos = desc.declared_capabilities().intersection(&supported);
// Note that we can't just do "include CGO if present" since it has prerequisites.
if mutual_protos.supports_named_subver(RELAY_CRYPT_CGO)
&& mutual_protos.supports_named_subver(RELAY_NEGOTIATE_SUBPROTO)
&& fc_protos.supports_named_subver(FLOWCTRL_CC)
{
subproto_request.push(RELAY_CRYPT_CGO);
}
let request_protos: Protocols = subproto_request.into_iter().collect();
let shared_protos = request_protos.union(&fc_protos);
PeerCaps {
shared_protos,
request_protos,
sendme_inc,
}
}
/// Add all relevant request extensions for this PeerCaps into `intro`.
pub(crate) fn add_extensions(&self, intro: &mut IntroduceHandshakePayload) {
use tor_cell::relaycell::extend::{CcRequest, SubprotocolRequest};
if self.sendme_inc.is_some() {
intro.add_extension(CcRequest::default().into());
}
if !self.request_protos.is_empty() {
let ext: SubprotocolRequest = REQUESTABLE_LIST
.iter()
.filter(|p| self.request_protos.supports_named_subver(**p))
.map(|p| NumberedSubver::from(*p))
.collect();
intro.add_extension(ext.into());
}
}
/// Return a list of the negotiable protocols that we share with the peer.
pub(crate) fn shared_protos(&self) -> Protocols {
self.shared_protos.clone()
}
/// Return the `sendme_inc` value that we decided to use.
///
/// Returns None if we aren't using congestion control.
pub(crate) fn cc_sendme_inc(&self) -> Option<u8> {
self.sendme_inc
}
}
#[cfg(not(feature = "negotiate-extensions"))]
impl PeerCaps {
/// Construct a new PeerCaps for a peer, given its descriptor.
pub(crate) fn new(_desc: &HsDesc) -> Self {
Self {}
}
/// Add all relevant request extensions for this PeerCaps into `intro`.
pub(crate) fn add_extensions(&self, _intro: &mut IntroduceHandshakePayload) {}
/// Return a list of the negotiable protocols that we share with the peer.
pub(crate) fn shared_protos(&self) -> Protocols {
// Note that we actually share all the must-implement client protocols.
// But we cannot assume that here; some protocols must be negotiated.
Protocols::new()
}
/// Return the `sendme_inc` value that we decided to use.
///
/// Returns None if we aren't using congestion control.
pub(crate) fn cc_sendme_inc(&self) -> Option<u8> {
None
}
}