mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-01-13 00:09:47 -05:00
receipt rework and discovery rework
This commit is contained in:
parent
d80a81e460
commit
b6e568f664
@ -6,15 +6,16 @@
|
||||
# -----------------------------------------------------------
|
||||
|
||||
---
|
||||
daemon:
|
||||
enabled: true
|
||||
pid_file: '/run/veilid-server.pid'
|
||||
working_directory: '/'
|
||||
user: veilid
|
||||
group: veilid
|
||||
logging:
|
||||
system:
|
||||
enabled: true
|
||||
level: info
|
||||
terminal:
|
||||
enabled: false
|
||||
core:
|
||||
protected_store:
|
||||
insecure_fallback_directory: '/var/db/veilid-server/protected_store'
|
||||
table_store:
|
||||
directory: '/var/db/veilid-server/table_store'
|
||||
block_store:
|
||||
directory: '/var/db/veilid-server/block_store'
|
@ -6,15 +6,15 @@ Requires=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/run/veilid-server.pid
|
||||
ExecStartPre=/usr/bin/rm -f /run/veilid-server.pid
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/veilid-server
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
KillSignal=SIGQUIT
|
||||
TimeoutStopSec=5
|
||||
KillMode=mixed
|
||||
PrivateTmp=true
|
||||
WorkingDirectory=/
|
||||
User=veilid
|
||||
Group=veilid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -111,12 +111,12 @@ struct NodeDialInfo {
|
||||
##############################
|
||||
|
||||
struct SignalInfoHolePunch {
|
||||
receipt @0 :Data; # receipt to return with hole punch
|
||||
receiptNonce @0 :Nonce; # receipt to return with hole punch
|
||||
peerInfo @1 :PeerInfo; # peer info of the signal sender for hole punch attempt
|
||||
}
|
||||
|
||||
struct SignalInfoReverseConnect {
|
||||
receipt @0 :Data; # receipt to return with reverse connect
|
||||
receiptNonce @0 :Nonce; # receipt to return with reverse connect
|
||||
peerInfo @1 :PeerInfo; # peer info of the signal sender for reverse connect attempt
|
||||
}
|
||||
|
||||
@ -217,8 +217,10 @@ struct ProtocolSet {
|
||||
struct NodeInfo {
|
||||
networkClass @0 :NetworkClass; # network class of this node
|
||||
outboundProtocols @1 :ProtocolSet; # protocols that can go outbound
|
||||
dialInfoDetailList @2 :List(DialInfoDetail); # inbound dial info details for this node
|
||||
relayPeerInfo @3 :PeerInfo; # (optional) relay peer info for this node
|
||||
minVersion @2 :UInt8; # minimum protocol version for rpc
|
||||
maxVersion @3 :UInt8; # maximum protocol version for rpc
|
||||
dialInfoDetailList @4 :List(DialInfoDetail); # inbound dial info details for this node
|
||||
relayPeerInfo @5 :PeerInfo; # (optional) relay peer info for this node
|
||||
}
|
||||
|
||||
struct SignedNodeInfo {
|
||||
@ -238,13 +240,15 @@ struct OperationStatusA {
|
||||
|
||||
struct OperationValidateDialInfo {
|
||||
dialInfo @0 :DialInfo; # dial info to use for the receipt
|
||||
receipt @1 :Data; # receipt to return to dial info to prove it is reachable
|
||||
redirect @2 :Bool; # request a different node do the validate
|
||||
alternatePort @3 :Bool; # return receipt from a different source port than the default
|
||||
minVersion @1 :UInt8; # minimum version for the direct receipt
|
||||
maxVersion @2 :UInt8; # maximum version for the direct receipt
|
||||
receiptNonce @3 :Nonce; # receipt to return to dial info to prove it is reachable
|
||||
redirect @4 :Bool; # request a different node do the validate
|
||||
}
|
||||
|
||||
struct OperationReturnReceipt {
|
||||
receipt @0 :Data; # receipt being returned to its origin
|
||||
receiptNonce @0 :Nonce; # receipt being returned to its origin
|
||||
extraData @1 :Data; # extra data for receipt
|
||||
}
|
||||
|
||||
struct OperationFindNodeQ {
|
||||
|
@ -78,8 +78,8 @@ impl Envelope {
|
||||
|
||||
pub fn from_signed_data(data: &[u8]) -> Result<Envelope, ()> {
|
||||
// Ensure we are at least the length of the envelope
|
||||
// Silent drop here, as we use zero length packets as part of the protocol for hole punching
|
||||
if data.len() < MIN_ENVELOPE_SIZE {
|
||||
trace!("envelope too small: len={}", data.len());
|
||||
return Err(());
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ use data_encoding::BASE64URL_NOPAD;
|
||||
// reserved: u8, // 0x05: Reserved for future use
|
||||
// size: u16, // 0x06: Total size of the receipt including the extra data and the signature. Maximum size is 1152 bytes.
|
||||
// nonce: [u8; 24], // 0x08: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required.
|
||||
// sender_id: [u8; 32], // 0x20: Node ID of the message source, which is the Ed25519 public key of the sender (must be verified with find_node if this is a new node_id/address combination)
|
||||
// sender_id: [u8; 32], // 0x20: Node ID of the message source, which is the Ed25519 public key of the sender
|
||||
// extra_data: [u8; ??], // 0x40: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1024 bytes)
|
||||
// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire receipt including header and extra data is appended to the packet
|
||||
// }
|
||||
|
@ -534,10 +534,16 @@ impl Network {
|
||||
|
||||
pub async fn tick(&self) -> Result<(), String> {
|
||||
let network_class = self.get_network_class().unwrap_or(NetworkClass::Invalid);
|
||||
let routing_table = self.routing_table();
|
||||
|
||||
// If we need to figure out our network class, tick the task for it
|
||||
if network_class == NetworkClass::Invalid {
|
||||
self.unlocked_inner.update_network_class_task.tick().await?;
|
||||
let rth = routing_table.get_routing_table_health();
|
||||
|
||||
// Need at least two entries to do this
|
||||
if rth.unreliable_entry_count + rth.reliable_entry_count >= 2 {
|
||||
self.unlocked_inner.update_network_class_task.tick().await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -16,9 +16,10 @@ struct DiscoveryContextInner {
|
||||
intf_addrs: Option<Vec<SocketAddress>>,
|
||||
protocol_type: Option<ProtocolType>,
|
||||
address_type: Option<AddressType>,
|
||||
external1_dial_info: Option<DialInfo>,
|
||||
external1: Option<SocketAddress>,
|
||||
node_b: Option<NodeRef>,
|
||||
// first node contacted
|
||||
external_1_dial_info: Option<DialInfo>,
|
||||
external_1_address: Option<SocketAddress>,
|
||||
node_1: Option<NodeRef>,
|
||||
// detected public dialinfo
|
||||
detected_network_class: Option<NetworkClass>,
|
||||
detected_public_dial_info: Option<DetectedPublicDialInfo>,
|
||||
@ -40,9 +41,9 @@ impl DiscoveryContext {
|
||||
intf_addrs: None,
|
||||
protocol_type: None,
|
||||
address_type: None,
|
||||
external1_dial_info: None,
|
||||
external1: None,
|
||||
node_b: None,
|
||||
external_1_dial_info: None,
|
||||
external_1_address: None,
|
||||
node_1: None,
|
||||
detected_network_class: None,
|
||||
detected_public_dial_info: None,
|
||||
})),
|
||||
@ -64,6 +65,7 @@ impl DiscoveryContext {
|
||||
}
|
||||
|
||||
// Ask for a public address check from a particular noderef
|
||||
// This is done over the normal port using RPC
|
||||
async fn request_public_address(&self, node_ref: NodeRef) -> Option<SocketAddress> {
|
||||
let rpc = self.routing_table.rpc_processor();
|
||||
rpc.rpc_call_status(node_ref.clone())
|
||||
@ -81,6 +83,7 @@ impl DiscoveryContext {
|
||||
}
|
||||
|
||||
// find fast peers with a particular address type, and ask them to tell us what our external address is
|
||||
// This is done over the normal port using RPC
|
||||
async fn discover_external_address(
|
||||
&self,
|
||||
protocol_type: ProtocolType,
|
||||
@ -109,6 +112,7 @@ impl DiscoveryContext {
|
||||
None
|
||||
}
|
||||
|
||||
// This pulls the already-detected local interface dial info from the routing table
|
||||
fn get_local_addresses(
|
||||
&self,
|
||||
protocol_type: ProtocolType,
|
||||
@ -135,10 +139,9 @@ impl DiscoveryContext {
|
||||
node_ref: NodeRef,
|
||||
dial_info: DialInfo,
|
||||
redirect: bool,
|
||||
alternate_port: bool,
|
||||
) -> bool {
|
||||
let rpc = self.routing_table.rpc_processor();
|
||||
rpc.rpc_call_validate_dial_info(node_ref.clone(), dial_info, redirect, alternate_port)
|
||||
rpc.rpc_call_validate_dial_info(node_ref.clone(), dial_info, redirect)
|
||||
.await
|
||||
.map_err(logthru_net!(
|
||||
"failed to send validate_dial_info to {:?}",
|
||||
@ -179,19 +182,20 @@ impl DiscoveryContext {
|
||||
inner.intf_addrs = Some(intf_addrs);
|
||||
inner.protocol_type = Some(protocol_type);
|
||||
inner.address_type = Some(address_type);
|
||||
inner.external1_dial_info = None;
|
||||
inner.external1 = None;
|
||||
inner.node_b = None;
|
||||
inner.external_1_dial_info = None;
|
||||
inner.external_1_address = None;
|
||||
inner.node_1 = None;
|
||||
}
|
||||
|
||||
// Get our first node's view of our external IP address via normal RPC
|
||||
pub async fn protocol_get_external_address_1(&self) -> bool {
|
||||
let (protocol_type, address_type) = {
|
||||
let inner = self.inner.lock();
|
||||
(inner.protocol_type.unwrap(), inner.address_type.unwrap())
|
||||
};
|
||||
|
||||
// Get our external address from some fast node, call it node B
|
||||
let (external1, node_b) = match self
|
||||
// Get our external address from some fast node, call it node 1
|
||||
let (external_1, node_1) = match self
|
||||
.discover_external_address(protocol_type, address_type, None)
|
||||
.await
|
||||
{
|
||||
@ -202,54 +206,56 @@ impl DiscoveryContext {
|
||||
}
|
||||
Some(v) => v,
|
||||
};
|
||||
let external1_dial_info = self.make_dial_info(external1, protocol_type);
|
||||
let external_1_dial_info = self.make_dial_info(external_1, protocol_type);
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
inner.external1_dial_info = Some(external1_dial_info);
|
||||
inner.external1 = Some(external1);
|
||||
inner.node_b = Some(node_b);
|
||||
inner.external_1_dial_info = Some(external_1_dial_info);
|
||||
inner.external_1_address = Some(external_1);
|
||||
inner.node_1 = Some(node_1);
|
||||
|
||||
log_net!(debug "external1_dial_info: {:?}\nexternal1: {:?}\nnode_b: {:?}", inner.external1_dial_info, inner.external1, inner.node_b);
|
||||
log_net!(debug "external_1_dial_info: {:?}\nexternal_1_address: {:?}\nnode_1: {:?}", inner.external_1_dial_info, inner.external_1_address, inner.node_1);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
// If we know we are not behind NAT, check our firewall status
|
||||
pub async fn protocol_process_no_nat(&self) -> Result<(), String> {
|
||||
let (node_b, external1_dial_info) = {
|
||||
let (node_b, external_1_dial_info) = {
|
||||
let inner = self.inner.lock();
|
||||
(
|
||||
inner.node_b.as_ref().unwrap().clone(),
|
||||
inner.external1_dial_info.as_ref().unwrap().clone(),
|
||||
inner.node_1.as_ref().unwrap().clone(),
|
||||
inner.external_1_dial_info.as_ref().unwrap().clone(),
|
||||
)
|
||||
};
|
||||
|
||||
// Do a validate_dial_info on the external address from a redirected node
|
||||
if self
|
||||
.validate_dial_info(node_b.clone(), external1_dial_info.clone(), true, false)
|
||||
.validate_dial_info(node_b.clone(), external_1_dial_info.clone(), true)
|
||||
.await
|
||||
{
|
||||
// Add public dial info with Direct dialinfo class
|
||||
self.set_detected_public_dial_info(external1_dial_info, DialInfoClass::Direct);
|
||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Direct);
|
||||
}
|
||||
// Attempt a UDP port mapping via all available and enabled mechanisms
|
||||
// Attempt a port mapping via all available and enabled mechanisms
|
||||
else if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
||||
// Got a port mapping, let's use it
|
||||
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
||||
} else {
|
||||
// Add public dial info with Blocked dialinfo class
|
||||
self.set_detected_public_dial_info(external1_dial_info, DialInfoClass::Blocked);
|
||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Blocked);
|
||||
}
|
||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// If we know we are behind NAT check what kind
|
||||
pub async fn protocol_process_nat(&self) -> Result<bool, String> {
|
||||
let (node_b, external1_dial_info, external1, protocol_type, address_type) = {
|
||||
let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = {
|
||||
let inner = self.inner.lock();
|
||||
(
|
||||
inner.node_b.as_ref().unwrap().clone(),
|
||||
inner.external1_dial_info.as_ref().unwrap().clone(),
|
||||
inner.external1.unwrap(),
|
||||
inner.node_1.as_ref().unwrap().clone(),
|
||||
inner.external_1_dial_info.as_ref().unwrap().clone(),
|
||||
inner.external_1_address.unwrap(),
|
||||
inner.protocol_type.unwrap(),
|
||||
inner.address_type.unwrap(),
|
||||
)
|
||||
@ -267,14 +273,14 @@ impl DiscoveryContext {
|
||||
|
||||
// Port mapping was not possible, let's see what kind of NAT we have
|
||||
|
||||
// Does a redirected dial info validation find us?
|
||||
// Does a redirected dial info validation from a different address and a random port find us?
|
||||
if self
|
||||
.validate_dial_info(node_b.clone(), external1_dial_info.clone(), true, false)
|
||||
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
||||
.await
|
||||
{
|
||||
// Yes, another machine can use the dial info directly, so Full Cone
|
||||
// Add public dial info with full cone NAT network class
|
||||
self.set_detected_public_dial_info(external1_dial_info, DialInfoClass::FullConeNAT);
|
||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::FullConeNAT);
|
||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||
|
||||
// No more retries
|
||||
@ -283,9 +289,9 @@ impl DiscoveryContext {
|
||||
|
||||
// No, we are restricted, determine what kind of restriction
|
||||
|
||||
// Get our external address from some fast node, that is not node B, call it node D
|
||||
let (external2, node_d) = match self
|
||||
.discover_external_address(protocol_type, address_type, Some(node_b.node_id()))
|
||||
// Get our external address from some fast node, that is not node 1, call it node 2
|
||||
let (external_2_address, node_2) = match self
|
||||
.discover_external_address(protocol_type, address_type, Some(node_1.node_id()))
|
||||
.await
|
||||
{
|
||||
None => {
|
||||
@ -296,7 +302,7 @@ impl DiscoveryContext {
|
||||
};
|
||||
|
||||
// If we have two different external addresses, then this is a symmetric NAT
|
||||
if external2 != external1 {
|
||||
if external_2_address != external_1_address {
|
||||
// Symmetric NAT is outbound only, no public dial info will work
|
||||
self.set_detected_network_class(NetworkClass::OutboundOnly);
|
||||
|
||||
@ -305,23 +311,22 @@ impl DiscoveryContext {
|
||||
}
|
||||
|
||||
// If we're going to end up as a restricted NAT of some sort
|
||||
|
||||
// Address is the same, so it's address or port restricted
|
||||
let external2_dial_info = DialInfo::udp(external2);
|
||||
// Do a validate_dial_info on the external address from a routed node
|
||||
|
||||
// Do a validate_dial_info on the external address from a random port
|
||||
if self
|
||||
.validate_dial_info(node_d.clone(), external2_dial_info.clone(), false, true)
|
||||
.validate_dial_info(node_2.clone(), external_1_dial_info.clone(), false)
|
||||
.await
|
||||
{
|
||||
// Got a reply from a non-default port, which means we're only address restricted
|
||||
self.set_detected_public_dial_info(
|
||||
external1_dial_info,
|
||||
external_1_dial_info,
|
||||
DialInfoClass::AddressRestrictedNAT,
|
||||
);
|
||||
} else {
|
||||
// Didn't get a reply from a non-default port, which means we are also port restricted
|
||||
self.set_detected_public_dial_info(
|
||||
external1_dial_info,
|
||||
external_1_dial_info,
|
||||
DialInfoClass::PortRestrictedNAT,
|
||||
);
|
||||
}
|
||||
@ -348,13 +353,13 @@ impl Network {
|
||||
|
||||
// Loop for restricted NAT retries
|
||||
loop {
|
||||
// Get our external address from some fast node, call it node B
|
||||
// Get our external address from some fast node, call it node 1
|
||||
if !context.protocol_get_external_address_1().await {
|
||||
// If we couldn't get an external address, then we should just try the whole network class detection again later
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If our local interface list contains external1 then there is no NAT in place
|
||||
// If our local interface list contains external_1 then there is no NAT in place
|
||||
{
|
||||
let res = {
|
||||
let inner = context.inner.lock();
|
||||
@ -362,7 +367,7 @@ impl Network {
|
||||
.intf_addrs
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains(inner.external1.as_ref().unwrap())
|
||||
.contains(inner.external_1_address.as_ref().unwrap())
|
||||
};
|
||||
if res {
|
||||
// No NAT
|
||||
@ -397,25 +402,25 @@ impl Network {
|
||||
// Start doing ipv6 protocol
|
||||
context.protocol_begin(protocol_type, AddressType::IPV6);
|
||||
|
||||
// Get our external address from some fast node, call it node B
|
||||
// Get our external address from some fast node, call it node 1
|
||||
if !context.protocol_get_external_address_1().await {
|
||||
// If we couldn't get an external address, then we should just try the whole network class detection again later
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If our local interface list doesn't contain external1 then there is an Ipv6 NAT in place
|
||||
// If our local interface list doesn't contain external_1 then there is an Ipv6 NAT in place
|
||||
{
|
||||
let inner = context.inner.lock();
|
||||
if !inner
|
||||
.intf_addrs
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains(inner.external1.as_ref().unwrap())
|
||||
.contains(inner.external_1_address.as_ref().unwrap())
|
||||
{
|
||||
// IPv6 NAT is not supported today
|
||||
log_net!(warn
|
||||
"IPv6 NAT is not supported for external address: {}",
|
||||
inner.external1.unwrap()
|
||||
inner.external_1_address.unwrap()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
@ -454,90 +459,90 @@ impl Network {
|
||||
.boxed(),
|
||||
);
|
||||
|
||||
// UDPv6
|
||||
unord.push(
|
||||
async {
|
||||
let udpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
if let Err(e) = self
|
||||
.update_ipv6_protocol_dialinfo(&udpv6_context, ProtocolType::UDP)
|
||||
.await
|
||||
{
|
||||
log_net!(debug "Failed UDPv6 dialinfo discovery: {}", e);
|
||||
return None;
|
||||
}
|
||||
Some(udpv6_context)
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
// // UDPv6
|
||||
// unord.push(
|
||||
// async {
|
||||
// let udpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
// if let Err(e) = self
|
||||
// .update_ipv6_protocol_dialinfo(&udpv6_context, ProtocolType::UDP)
|
||||
// .await
|
||||
// {
|
||||
// log_net!(debug "Failed UDPv6 dialinfo discovery: {}", e);
|
||||
// return None;
|
||||
// }
|
||||
// Some(udpv6_context)
|
||||
// }
|
||||
// .boxed(),
|
||||
// );
|
||||
}
|
||||
|
||||
if protocol_config.inbound.contains(ProtocolType::TCP) {
|
||||
// TCPv4
|
||||
unord.push(
|
||||
async {
|
||||
let tcpv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
if let Err(e) = self
|
||||
.update_ipv4_protocol_dialinfo(&tcpv4_context, ProtocolType::TCP)
|
||||
.await
|
||||
{
|
||||
log_net!(debug "Failed TCPv4 dialinfo discovery: {}", e);
|
||||
return None;
|
||||
}
|
||||
Some(tcpv4_context)
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
// if protocol_config.inbound.contains(ProtocolType::TCP) {
|
||||
// // TCPv4
|
||||
// unord.push(
|
||||
// async {
|
||||
// let tcpv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
// if let Err(e) = self
|
||||
// .update_ipv4_protocol_dialinfo(&tcpv4_context, ProtocolType::TCP)
|
||||
// .await
|
||||
// {
|
||||
// log_net!(debug "Failed TCPv4 dialinfo discovery: {}", e);
|
||||
// return None;
|
||||
// }
|
||||
// Some(tcpv4_context)
|
||||
// }
|
||||
// .boxed(),
|
||||
// );
|
||||
|
||||
// TCPv6
|
||||
unord.push(
|
||||
async {
|
||||
let tcpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
if let Err(e) = self
|
||||
.update_ipv6_protocol_dialinfo(&tcpv6_context, ProtocolType::TCP)
|
||||
.await
|
||||
{
|
||||
log_net!(debug "Failed TCPv6 dialinfo discovery: {}", e);
|
||||
return None;
|
||||
}
|
||||
Some(tcpv6_context)
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
// // TCPv6
|
||||
// unord.push(
|
||||
// async {
|
||||
// let tcpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
// if let Err(e) = self
|
||||
// .update_ipv6_protocol_dialinfo(&tcpv6_context, ProtocolType::TCP)
|
||||
// .await
|
||||
// {
|
||||
// log_net!(debug "Failed TCPv6 dialinfo discovery: {}", e);
|
||||
// return None;
|
||||
// }
|
||||
// Some(tcpv6_context)
|
||||
// }
|
||||
// .boxed(),
|
||||
// );
|
||||
// }
|
||||
|
||||
if protocol_config.inbound.contains(ProtocolType::WS) {
|
||||
// WS4
|
||||
unord.push(
|
||||
async {
|
||||
let wsv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
if let Err(e) = self
|
||||
.update_ipv4_protocol_dialinfo(&wsv4_context, ProtocolType::WS)
|
||||
.await
|
||||
{
|
||||
log_net!(debug "Failed WSv4 dialinfo discovery: {}", e);
|
||||
return None;
|
||||
}
|
||||
Some(wsv4_context)
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
// if protocol_config.inbound.contains(ProtocolType::WS) {
|
||||
// // WS4
|
||||
// unord.push(
|
||||
// async {
|
||||
// let wsv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
// if let Err(e) = self
|
||||
// .update_ipv4_protocol_dialinfo(&wsv4_context, ProtocolType::WS)
|
||||
// .await
|
||||
// {
|
||||
// log_net!(debug "Failed WSv4 dialinfo discovery: {}", e);
|
||||
// return None;
|
||||
// }
|
||||
// Some(wsv4_context)
|
||||
// }
|
||||
// .boxed(),
|
||||
// );
|
||||
|
||||
// WSv6
|
||||
unord.push(
|
||||
async {
|
||||
let wsv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
if let Err(e) = self
|
||||
.update_ipv6_protocol_dialinfo(&wsv6_context, ProtocolType::TCP)
|
||||
.await
|
||||
{
|
||||
log_net!(debug "Failed WSv6 dialinfo discovery: {}", e);
|
||||
return None;
|
||||
}
|
||||
Some(wsv6_context)
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
// // WSv6
|
||||
// unord.push(
|
||||
// async {
|
||||
// let wsv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||
// if let Err(e) = self
|
||||
// .update_ipv6_protocol_dialinfo(&wsv6_context, ProtocolType::TCP)
|
||||
// .await
|
||||
// {
|
||||
// log_net!(debug "Failed WSv6 dialinfo discovery: {}", e);
|
||||
// return None;
|
||||
// }
|
||||
// Some(wsv6_context)
|
||||
// }
|
||||
// .boxed(),
|
||||
// );
|
||||
// }
|
||||
|
||||
// Wait for all discovery futures to complete and collect contexts
|
||||
let mut contexts = Vec::<DiscoveryContext>::new();
|
||||
|
@ -111,11 +111,13 @@ impl WebsocketProtocolHandler {
|
||||
data.len(),
|
||||
dial_info,
|
||||
);
|
||||
|
||||
|
||||
// Make the real connection
|
||||
let conn = Self::connect(None, dial_info)
|
||||
.await
|
||||
.map_err(|e| format!("failed to connect websocket for unbound message: {}", e))?;
|
||||
|
||||
conn.send(data).await
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -421,96 +421,80 @@ impl NetworkManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Generates an out-of-band receipt
|
||||
pub fn generate_receipt<D: AsRef<[u8]>>(
|
||||
// Generates a multi-shot/normal receipt
|
||||
pub fn generate_receipt(
|
||||
&self,
|
||||
expiration_us: u64,
|
||||
expected_returns: u32,
|
||||
extra_data: D,
|
||||
callback: impl ReceiptCallback,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
) -> Result<ReceiptNonce, String> {
|
||||
let receipt_manager = self.receipt_manager();
|
||||
let routing_table = self.routing_table();
|
||||
|
||||
// Generate receipt and serialized form to return
|
||||
let nonce = Crypto::get_random_nonce();
|
||||
let receipt = Receipt::try_new(0, nonce, routing_table.node_id(), extra_data)?;
|
||||
let out = receipt
|
||||
.to_signed_data(&routing_table.node_id_secret())
|
||||
.map_err(|_| "failed to generate signed receipt".to_owned())?;
|
||||
// Generate receipt nonce
|
||||
let receipt_nonce = Crypto::get_random_nonce();
|
||||
|
||||
// Record the receipt for later
|
||||
let exp_ts = intf::get_timestamp() + expiration_us;
|
||||
receipt_manager.record_receipt(receipt, exp_ts, expected_returns, callback);
|
||||
receipt_manager.record_receipt(receipt_nonce, exp_ts, expected_returns, callback);
|
||||
|
||||
Ok(out)
|
||||
Ok(receipt_nonce)
|
||||
}
|
||||
|
||||
pub fn generate_single_shot_receipt<D: AsRef<[u8]>>(
|
||||
// Generates a single-shot/normal receipt
|
||||
pub fn generate_single_shot_receipt(
|
||||
&self,
|
||||
expiration_us: u64,
|
||||
extra_data: D,
|
||||
) -> Result<(Vec<u8>, EventualValueFuture<ReceiptEvent>), String> {
|
||||
) -> Result<(ReceiptNonce, EventualValueFuture<ReceiptEvent>), String> {
|
||||
let receipt_manager = self.receipt_manager();
|
||||
let routing_table = self.routing_table();
|
||||
|
||||
// Generate receipt and serialized form to return
|
||||
let nonce = Crypto::get_random_nonce();
|
||||
let receipt = Receipt::try_new(0, nonce, routing_table.node_id(), extra_data)?;
|
||||
let out = receipt
|
||||
.to_signed_data(&routing_table.node_id_secret())
|
||||
.map_err(|_| "failed to generate signed receipt".to_owned())?;
|
||||
// Generate receipt nonce
|
||||
let receipt_nonce = Crypto::get_random_nonce();
|
||||
|
||||
// Record the receipt for later
|
||||
let exp_ts = intf::get_timestamp() + expiration_us;
|
||||
let eventual = SingleShotEventual::new(Some(ReceiptEvent::Cancelled));
|
||||
let instance = eventual.instance();
|
||||
receipt_manager.record_single_shot_receipt(receipt, exp_ts, eventual);
|
||||
receipt_manager.record_single_shot_receipt(receipt_nonce, exp_ts, eventual);
|
||||
|
||||
Ok((out, instance))
|
||||
Ok((receipt_nonce, instance))
|
||||
}
|
||||
|
||||
// Process a received out-of-band receipt
|
||||
pub async fn handle_out_of_band_receipt<R: AsRef<[u8]>>(
|
||||
&self,
|
||||
receipt_data: R,
|
||||
descriptor: ConnectionDescriptor,
|
||||
) -> Result<(), String> {
|
||||
let routing_table = self.routing_table();
|
||||
let receipt_manager = self.receipt_manager();
|
||||
let ts = intf::get_timestamp();
|
||||
|
||||
let receipt = Receipt::from_signed_data(receipt_data.as_ref())
|
||||
.map_err(|_| "failed to parse signed receipt".to_owned())?;
|
||||
|
||||
// Cache the receipt information in the routing table
|
||||
let source_noderef = routing_table
|
||||
.register_node_with_existing_connection(receipt.get_sender_id(), descriptor, ts)
|
||||
.map_err(|e| format!("node id registration from receipt failed: {}", e))?;
|
||||
|
||||
receipt_manager
|
||||
.handle_receipt(source_noderef, receipt)
|
||||
.handle_receipt(receipt.get_nonce(), receipt.get_extra_data().to_vec(), None)
|
||||
.await
|
||||
}
|
||||
|
||||
// Process a received in-band receipt
|
||||
pub async fn handle_in_band_receipt<R: AsRef<[u8]>>(
|
||||
pub async fn handle_in_band_receipt(
|
||||
&self,
|
||||
receipt_data: R,
|
||||
receipt_nonce: ReceiptNonce,
|
||||
extra_data: Vec<u8>,
|
||||
inbound_nr: NodeRef,
|
||||
) -> Result<(), String> {
|
||||
let receipt_manager = self.receipt_manager();
|
||||
|
||||
let receipt = Receipt::from_signed_data(receipt_data.as_ref())
|
||||
.map_err(|_| "failed to parse signed receipt".to_owned())?;
|
||||
|
||||
receipt_manager.handle_receipt(inbound_nr, receipt).await
|
||||
receipt_manager
|
||||
.handle_receipt(receipt_nonce, extra_data, Some(inbound_nr))
|
||||
.await
|
||||
}
|
||||
|
||||
// Process a received signal
|
||||
pub async fn handle_signal(&self, signal_info: SignalInfo) -> Result<(), String> {
|
||||
match signal_info {
|
||||
SignalInfo::ReverseConnect { receipt, peer_info } => {
|
||||
SignalInfo::ReverseConnect {
|
||||
receipt_nonce,
|
||||
peer_info,
|
||||
} => {
|
||||
let routing_table = self.routing_table();
|
||||
let rpc = self.rpc_processor();
|
||||
|
||||
@ -521,12 +505,16 @@ impl NetworkManager {
|
||||
)?;
|
||||
|
||||
// Make a reverse connection to the peer and send the receipt to it
|
||||
rpc.rpc_call_return_receipt(Destination::Direct(peer_nr), None, receipt)
|
||||
rpc.rpc_call_return_receipt(Destination::Direct(peer_nr), None, receipt_nonce, [])
|
||||
.await
|
||||
.map_err(map_to_string)?;
|
||||
}
|
||||
SignalInfo::HolePunch { receipt, peer_info } => {
|
||||
SignalInfo::HolePunch {
|
||||
receipt_nonce,
|
||||
peer_info,
|
||||
} => {
|
||||
let routing_table = self.routing_table();
|
||||
let rpc = self.rpc_processor();
|
||||
|
||||
// Add the peer info to our routing table
|
||||
let mut peer_nr = routing_table.register_node_with_signed_node_info(
|
||||
@ -540,6 +528,12 @@ impl NetworkManager {
|
||||
.first_filtered_dial_info_detail(Some(RoutingDomain::PublicInternet))
|
||||
.ok_or_else(|| "No hole punch capable dialinfo found for node".to_owned())?;
|
||||
|
||||
// Now that we picked a specific dialinfo, further restrict the noderef to the specific address type
|
||||
let mut filter = peer_nr.take_filter().unwrap();
|
||||
filter.peer_scope = PeerScope::Global;
|
||||
filter.address_type = Some(hole_punch_dial_info_detail.dial_info.address_type());
|
||||
peer_nr.set_filter(Some(filter));
|
||||
|
||||
// Do our half of the hole punch by sending an empty packet
|
||||
// Both sides will do this and then the receipt will get sent over the punched hole
|
||||
self.net()
|
||||
@ -551,8 +545,8 @@ impl NetworkManager {
|
||||
|
||||
// XXX: do we need a delay here? or another hole punch packet?
|
||||
|
||||
// Return the receipt over the direct channel since we want to use exactly the same dial info
|
||||
self.send_direct_receipt(hole_punch_dial_info_detail.dial_info, receipt, false)
|
||||
// Return the receipt using the same dial info send the receipt to it
|
||||
rpc.rpc_call_return_receipt(Destination::Direct(peer_nr), None, receipt_nonce, [])
|
||||
.await
|
||||
.map_err(map_to_string)?;
|
||||
}
|
||||
@ -639,26 +633,26 @@ impl NetworkManager {
|
||||
}
|
||||
|
||||
// Called by the RPC handler when we want to issue an direct receipt
|
||||
pub async fn send_direct_receipt<B: AsRef<[u8]>>(
|
||||
pub async fn send_out_of_band_receipt<D: AsRef<[u8]>>(
|
||||
&self,
|
||||
dial_info: DialInfo,
|
||||
rcpt_data: B,
|
||||
alternate_port: bool,
|
||||
version: u8,
|
||||
receipt_nonce: Nonce,
|
||||
extra_data: D,
|
||||
) -> Result<(), String> {
|
||||
// Validate receipt before we send it, otherwise this may be arbitrary data!
|
||||
let _ = Receipt::from_signed_data(rcpt_data.as_ref())
|
||||
.map_err(|_| "failed to validate direct receipt".to_owned())?;
|
||||
let routing_table = self.routing_table();
|
||||
let node_id = routing_table.node_id();
|
||||
let node_id_secret = routing_table.node_id_secret();
|
||||
|
||||
let receipt = Receipt::try_new(version, receipt_nonce, node_id, extra_data)?;
|
||||
let rcpt_data = receipt
|
||||
.to_signed_data(&node_id_secret)
|
||||
.map_err(|_| "failed to sign receipt".to_owned())?;
|
||||
|
||||
// Send receipt directly
|
||||
if alternate_port {
|
||||
self.net()
|
||||
.send_data_unbound_to_dial_info(dial_info, rcpt_data.as_ref().to_vec())
|
||||
.await
|
||||
} else {
|
||||
self.net()
|
||||
.send_data_to_dial_info(dial_info, rcpt_data.as_ref().to_vec())
|
||||
.await
|
||||
}
|
||||
self.net()
|
||||
.send_data_unbound_to_dial_info(dial_info, rcpt_data)
|
||||
.await
|
||||
}
|
||||
|
||||
// Figure out how to reach a node
|
||||
@ -790,8 +784,8 @@ impl NetworkManager {
|
||||
// Build a return receipt for the signal
|
||||
let receipt_timeout =
|
||||
ms_to_us(self.config.get().network.reverse_connection_receipt_time_ms);
|
||||
let (receipt, eventual_value) = self
|
||||
.generate_single_shot_receipt(receipt_timeout, [])
|
||||
let (receipt_nonce, eventual_value) = self
|
||||
.generate_single_shot_receipt(receipt_timeout)
|
||||
.map_err(map_to_string)?;
|
||||
|
||||
// Get our peer info
|
||||
@ -802,14 +796,23 @@ impl NetworkManager {
|
||||
rpc.rpc_call_signal(
|
||||
Destination::Relay(relay_nr.clone(), target_nr.node_id()),
|
||||
None,
|
||||
SignalInfo::ReverseConnect { receipt, peer_info },
|
||||
SignalInfo::ReverseConnect {
|
||||
receipt_nonce,
|
||||
peer_info,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(logthru_net!("failed to send signal to {:?}", relay_nr))
|
||||
.map_err(map_to_string)?;
|
||||
// Wait for the return receipt
|
||||
let inbound_nr = match eventual_value.await.take_value().unwrap() {
|
||||
ReceiptEvent::Returned(inbound_nr) => inbound_nr,
|
||||
ReceiptEvent::ReturnedOutOfBand { extra_data: _ } => {
|
||||
return Err("reverse connect receipt should be returned in-band".to_owned());
|
||||
}
|
||||
ReceiptEvent::ReturnedInBand {
|
||||
inbound_noderef,
|
||||
extra_data: _,
|
||||
} => inbound_noderef,
|
||||
ReceiptEvent::Expired => {
|
||||
return Err(format!(
|
||||
"reverse connect receipt expired from {:?}",
|
||||
@ -867,8 +870,8 @@ impl NetworkManager {
|
||||
// Build a return receipt for the signal
|
||||
let receipt_timeout =
|
||||
ms_to_us(self.config.get().network.reverse_connection_receipt_time_ms);
|
||||
let (receipt, eventual_value) = self
|
||||
.generate_single_shot_receipt(receipt_timeout, [])
|
||||
let (receipt_nonce, eventual_value) = self
|
||||
.generate_single_shot_receipt(receipt_timeout)
|
||||
.map_err(map_to_string)?;
|
||||
|
||||
// Get our peer info
|
||||
@ -890,7 +893,10 @@ impl NetworkManager {
|
||||
rpc.rpc_call_signal(
|
||||
Destination::Relay(relay_nr.clone(), target_nr.node_id()),
|
||||
None,
|
||||
SignalInfo::HolePunch { receipt, peer_info },
|
||||
SignalInfo::HolePunch {
|
||||
receipt_nonce,
|
||||
peer_info,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(logthru_net!("failed to send signal to {:?}", relay_nr))
|
||||
@ -898,19 +904,28 @@ impl NetworkManager {
|
||||
|
||||
// Wait for the return receipt
|
||||
let inbound_nr = match eventual_value.await.take_value().unwrap() {
|
||||
ReceiptEvent::Returned(inbound_nr) => inbound_nr,
|
||||
ReceiptEvent::ReturnedOutOfBand { extra_data: _ } => {
|
||||
return Err("hole punch receipt should be returned in-band".to_owned());
|
||||
}
|
||||
ReceiptEvent::ReturnedInBand {
|
||||
inbound_noderef,
|
||||
extra_data: _,
|
||||
} => inbound_noderef,
|
||||
ReceiptEvent::Expired => {
|
||||
return Err(format!("hole punch receipt expired from {:?}", target_nr));
|
||||
return Err(format!("hole punch receipt expired from {}", target_nr));
|
||||
}
|
||||
ReceiptEvent::Cancelled => {
|
||||
return Err(format!("hole punch receipt cancelled from {:?}", target_nr));
|
||||
return Err(format!("hole punch receipt cancelled from {}", target_nr));
|
||||
}
|
||||
};
|
||||
|
||||
// We expect the inbound noderef to be the same as the target noderef
|
||||
// if they aren't the same, we should error on this and figure out what then hell is up
|
||||
if target_nr != inbound_nr {
|
||||
error!("unexpected noderef mismatch on hole punch");
|
||||
return Err(format!(
|
||||
"unexpected noderef mismatch on hole punch {}, expected {}",
|
||||
inbound_nr, target_nr
|
||||
));
|
||||
}
|
||||
|
||||
// And now use the existing connection to send over
|
||||
@ -1020,9 +1035,14 @@ impl NetworkManager {
|
||||
// Network accounting
|
||||
self.stats_packet_rcvd(descriptor.remote.to_socket_addr().ip(), data.len() as u64);
|
||||
|
||||
// Ensure we can read the magic number
|
||||
if data.len() < 4 {
|
||||
return Err("short packet".to_owned());
|
||||
}
|
||||
|
||||
// Is this an out-of-band receipt instead of an envelope?
|
||||
if data[0..4] == *RECEIPT_MAGIC {
|
||||
self.handle_out_of_band_receipt(data, descriptor).await?;
|
||||
self.handle_out_of_band_receipt(data).await?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,13 @@ use xx::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ReceiptEvent {
|
||||
Returned(NodeRef),
|
||||
ReturnedOutOfBand {
|
||||
extra_data: Vec<u8>,
|
||||
},
|
||||
ReturnedInBand {
|
||||
inbound_noderef: NodeRef,
|
||||
extra_data: Vec<u8>,
|
||||
},
|
||||
Expired,
|
||||
Cancelled,
|
||||
}
|
||||
@ -108,29 +114,29 @@ impl fmt::Debug for ReceiptRecord {
|
||||
}
|
||||
|
||||
impl ReceiptRecord {
|
||||
pub fn from_receipt(
|
||||
receipt: &Receipt,
|
||||
pub fn new(
|
||||
receipt_nonce: ReceiptNonce,
|
||||
expiration_ts: u64,
|
||||
expected_returns: u32,
|
||||
receipt_callback: impl ReceiptCallback,
|
||||
) -> Self {
|
||||
Self {
|
||||
expiration_ts,
|
||||
nonce: receipt.get_nonce(),
|
||||
nonce: receipt_nonce,
|
||||
expected_returns,
|
||||
returns_so_far: 0u32,
|
||||
receipt_callback: ReceiptRecordCallbackType::Normal(Box::new(receipt_callback)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_single_shot_receipt(
|
||||
receipt: &Receipt,
|
||||
pub fn new_single_shot(
|
||||
receipt_nonce: ReceiptNonce,
|
||||
expiration_ts: u64,
|
||||
eventual: ReceiptSingleShotType,
|
||||
) -> Self {
|
||||
Self {
|
||||
expiration_ts,
|
||||
nonce: receipt.get_nonce(),
|
||||
nonce: receipt_nonce,
|
||||
returns_so_far: 0u32,
|
||||
expected_returns: 1u32,
|
||||
receipt_callback: ReceiptRecordCallbackType::SingleShot(Some(eventual)),
|
||||
@ -167,7 +173,7 @@ impl PartialOrd for ReceiptRecordTimestampSort {
|
||||
|
||||
pub struct ReceiptManagerInner {
|
||||
network_manager: NetworkManager,
|
||||
receipts_by_nonce: BTreeMap<ReceiptNonce, Arc<Mutex<ReceiptRecord>>>,
|
||||
records_by_nonce: BTreeMap<ReceiptNonce, Arc<Mutex<ReceiptRecord>>>,
|
||||
next_oldest_ts: Option<u64>,
|
||||
timeout_task: SingleFuture<()>,
|
||||
}
|
||||
@ -181,7 +187,7 @@ impl ReceiptManager {
|
||||
fn new_inner(network_manager: NetworkManager) -> ReceiptManagerInner {
|
||||
ReceiptManagerInner {
|
||||
network_manager,
|
||||
receipts_by_nonce: BTreeMap::new(),
|
||||
records_by_nonce: BTreeMap::new(),
|
||||
next_oldest_ts: None,
|
||||
timeout_task: SingleFuture::new(),
|
||||
}
|
||||
@ -240,7 +246,7 @@ impl ReceiptManager {
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
let mut expired_nonces = Vec::new();
|
||||
for (k, v) in &inner.receipts_by_nonce {
|
||||
for (k, v) in &inner.records_by_nonce {
|
||||
let receipt_inner = v.lock();
|
||||
if receipt_inner.expiration_ts <= now {
|
||||
// Expire this receipt
|
||||
@ -257,10 +263,7 @@ impl ReceiptManager {
|
||||
}
|
||||
// Now remove the expired receipts
|
||||
for e in expired_nonces {
|
||||
let expired_record = inner
|
||||
.receipts_by_nonce
|
||||
.remove(&e)
|
||||
.expect("key should exist");
|
||||
let expired_record = inner.records_by_nonce.remove(&e).expect("key should exist");
|
||||
expired_records.push(expired_record);
|
||||
}
|
||||
// Update the next oldest timestamp
|
||||
@ -305,37 +308,39 @@ impl ReceiptManager {
|
||||
|
||||
pub fn record_receipt(
|
||||
&self,
|
||||
receipt: Receipt,
|
||||
receipt_nonce: ReceiptNonce,
|
||||
expiration: u64,
|
||||
expected_returns: u32,
|
||||
callback: impl ReceiptCallback,
|
||||
) {
|
||||
log_rpc!(debug "== New Multiple Receipt ({}) {} ", expected_returns, receipt.get_nonce().encode());
|
||||
let record = Arc::new(Mutex::new(ReceiptRecord::from_receipt(
|
||||
&receipt,
|
||||
log_rpc!(debug "== New Multiple Receipt ({}) {} ", expected_returns, receipt_nonce.encode());
|
||||
let record = Arc::new(Mutex::new(ReceiptRecord::new(
|
||||
receipt_nonce,
|
||||
expiration,
|
||||
expected_returns,
|
||||
callback,
|
||||
)));
|
||||
let mut inner = self.inner.lock();
|
||||
inner.receipts_by_nonce.insert(receipt.get_nonce(), record);
|
||||
inner.records_by_nonce.insert(receipt_nonce, record);
|
||||
|
||||
Self::update_next_oldest_timestamp(&mut *inner);
|
||||
}
|
||||
|
||||
pub fn record_single_shot_receipt(
|
||||
&self,
|
||||
receipt: Receipt,
|
||||
receipt_nonce: ReceiptNonce,
|
||||
expiration: u64,
|
||||
eventual: ReceiptSingleShotType,
|
||||
) {
|
||||
log_rpc!(debug "== New SingleShot Receipt {}", receipt.get_nonce().encode());
|
||||
log_rpc!(debug "== New SingleShot Receipt {}", receipt_nonce.encode());
|
||||
|
||||
let record = Arc::new(Mutex::new(ReceiptRecord::from_single_shot_receipt(
|
||||
&receipt, expiration, eventual,
|
||||
let record = Arc::new(Mutex::new(ReceiptRecord::new_single_shot(
|
||||
receipt_nonce,
|
||||
expiration,
|
||||
eventual,
|
||||
)));
|
||||
let mut inner = self.inner.lock();
|
||||
inner.receipts_by_nonce.insert(receipt.get_nonce(), record);
|
||||
inner.records_by_nonce.insert(receipt_nonce, record);
|
||||
|
||||
Self::update_next_oldest_timestamp(&mut *inner);
|
||||
}
|
||||
@ -343,7 +348,7 @@ impl ReceiptManager {
|
||||
fn update_next_oldest_timestamp(inner: &mut ReceiptManagerInner) {
|
||||
// Update the next oldest timestamp
|
||||
let mut new_next_oldest_ts: Option<u64> = None;
|
||||
for v in inner.receipts_by_nonce.values() {
|
||||
for v in inner.records_by_nonce.values() {
|
||||
let receipt_inner = v.lock();
|
||||
if new_next_oldest_ts.is_none()
|
||||
|| receipt_inner.expiration_ts < new_next_oldest_ts.unwrap()
|
||||
@ -362,7 +367,7 @@ impl ReceiptManager {
|
||||
// Remove the record
|
||||
let record = {
|
||||
let mut inner = self.inner.lock();
|
||||
let record = match inner.receipts_by_nonce.remove(nonce) {
|
||||
let record = match inner.records_by_nonce.remove(nonce) {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
return Err("receipt not recorded".to_owned());
|
||||
@ -386,14 +391,31 @@ impl ReceiptManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_receipt(&self, node_ref: NodeRef, receipt: Receipt) -> Result<(), String> {
|
||||
log_rpc!(debug "<<== RECEIPT {} <- {}", receipt.get_nonce().encode(), node_ref);
|
||||
pub async fn handle_receipt(
|
||||
&self,
|
||||
receipt_nonce: ReceiptNonce,
|
||||
extra_data: Vec<u8>,
|
||||
inbound_noderef: Option<NodeRef>,
|
||||
) -> Result<(), String> {
|
||||
log_rpc!(debug "<<== RECEIPT {} <- {}{}",
|
||||
receipt_nonce.encode(),
|
||||
if let Some(nr) = &inbound_noderef {
|
||||
nr.to_string()
|
||||
} else {
|
||||
"DIRECT".to_owned()
|
||||
},
|
||||
if extra_data.is_empty() {
|
||||
"".to_owned()
|
||||
} else {
|
||||
format!("[{} extra]", extra_data.len())
|
||||
}
|
||||
);
|
||||
|
||||
// Increment return count
|
||||
let callback_future = {
|
||||
// Look up the receipt record from the nonce
|
||||
let mut inner = self.inner.lock();
|
||||
let record = match inner.receipts_by_nonce.get(&receipt.get_nonce()) {
|
||||
let record = match inner.records_by_nonce.get(&receipt_nonce) {
|
||||
Some(r) => r.clone(),
|
||||
None => {
|
||||
return Err("receipt not recorded".to_owned());
|
||||
@ -402,12 +424,22 @@ impl ReceiptManager {
|
||||
// Generate the callback future
|
||||
let mut record_mut = record.lock();
|
||||
record_mut.returns_so_far += 1;
|
||||
let callback_future =
|
||||
Self::perform_callback(ReceiptEvent::Returned(node_ref), &mut record_mut);
|
||||
|
||||
// Get the receipt event to return
|
||||
let receipt_event = if let Some(inbound_noderef) = inbound_noderef {
|
||||
ReceiptEvent::ReturnedInBand {
|
||||
inbound_noderef,
|
||||
extra_data,
|
||||
}
|
||||
} else {
|
||||
ReceiptEvent::ReturnedOutOfBand { extra_data }
|
||||
};
|
||||
|
||||
let callback_future = Self::perform_callback(receipt_event, &mut record_mut);
|
||||
|
||||
// Remove the record if we're done
|
||||
if record_mut.returns_so_far == record_mut.expected_returns {
|
||||
inner.receipts_by_nonce.remove(&receipt.get_nonce());
|
||||
inner.records_by_nonce.remove(&receipt_nonce);
|
||||
|
||||
Self::update_next_oldest_timestamp(&mut *inner);
|
||||
}
|
||||
|
@ -143,6 +143,10 @@ impl BucketEntry {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.min_max_version = Some((
|
||||
signed_node_info.node_info.min_version,
|
||||
signed_node_info.node_info.max_version,
|
||||
));
|
||||
self.opt_signed_node_info = Some(signed_node_info);
|
||||
}
|
||||
pub fn update_local_node_info(&mut self, local_node_info: LocalNodeInfo) {
|
||||
|
@ -30,18 +30,41 @@ impl RoutingTable {
|
||||
if gdis.is_empty() {
|
||||
out += "No TXT Record\n";
|
||||
} else {
|
||||
out += "TXT Record:\n";
|
||||
out += &self.node_id().encode();
|
||||
|
||||
let mut urls = Vec::new();
|
||||
let mut short_urls = Vec::new();
|
||||
let mut some_hostname = Option::<String>::None;
|
||||
for gdi in gdis {
|
||||
urls.push(gdi.dial_info.to_url().await);
|
||||
}
|
||||
urls.sort();
|
||||
urls.dedup();
|
||||
let (short_url, hostname) = gdi.dial_info.to_short().await;
|
||||
if let Some(h) = &some_hostname {
|
||||
if h != &hostname {
|
||||
return format!(
|
||||
"Inconsistent hostnames for dial info: {} vs {}",
|
||||
some_hostname.unwrap(),
|
||||
hostname
|
||||
);
|
||||
}
|
||||
} else {
|
||||
some_hostname = Some(hostname);
|
||||
}
|
||||
|
||||
for url in urls {
|
||||
out += &format!(",{}", url);
|
||||
short_urls.push(short_url);
|
||||
}
|
||||
if some_hostname.is_none() || short_urls.is_empty() {
|
||||
return "No dial info for bootstrap host".to_owned();
|
||||
}
|
||||
short_urls.sort();
|
||||
short_urls.dedup();
|
||||
|
||||
out += "TXT Record:\n";
|
||||
out += &format!(
|
||||
"{},{},{},{},{}",
|
||||
BOOTSTRAP_TXT_VERSION,
|
||||
MIN_VERSION,
|
||||
MAX_VERSION,
|
||||
self.node_id().encode(),
|
||||
some_hostname.unwrap()
|
||||
);
|
||||
for short_url in short_urls {
|
||||
out += &format!(",{}", short_url);
|
||||
}
|
||||
out += "\n";
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ impl RoutingTable {
|
||||
NodeInfo {
|
||||
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
|
||||
outbound_protocols: netman.get_protocol_config().unwrap_or_default().outbound,
|
||||
min_version: MIN_VERSION,
|
||||
max_version: MAX_VERSION,
|
||||
dial_info_detail_list: self.dial_info_details(RoutingDomain::PublicInternet),
|
||||
relay_peer_info: relay_node.and_then(|rn| rn.peer_info().map(Box::new)),
|
||||
}
|
||||
|
@ -22,6 +22,16 @@ pub use stats_accounting::*;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub const BOOTSTRAP_TXT_VERSION: u8 = 0;
|
||||
|