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:
|
logging:
|
||||||
system:
|
system:
|
||||||
enabled: true
|
enabled: true
|
||||||
level: info
|
level: info
|
||||||
terminal:
|
terminal:
|
||||||
enabled: false
|
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
|
After=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=forking
|
Type=simple
|
||||||
PIDFile=/run/veilid-server.pid
|
|
||||||
ExecStartPre=/usr/bin/rm -f /run/veilid-server.pid
|
|
||||||
ExecStart=/usr/bin/veilid-server
|
ExecStart=/usr/bin/veilid-server
|
||||||
ExecReload=/bin/kill -s HUP $MAINPID
|
ExecReload=/bin/kill -s HUP $MAINPID
|
||||||
KillSignal=SIGQUIT
|
KillSignal=SIGQUIT
|
||||||
TimeoutStopSec=5
|
TimeoutStopSec=5
|
||||||
KillMode=mixed
|
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
|
WorkingDirectory=/
|
||||||
|
User=veilid
|
||||||
|
Group=veilid
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
@ -111,12 +111,12 @@ struct NodeDialInfo {
|
|||||||
##############################
|
##############################
|
||||||
|
|
||||||
struct SignalInfoHolePunch {
|
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
|
peerInfo @1 :PeerInfo; # peer info of the signal sender for hole punch attempt
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SignalInfoReverseConnect {
|
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
|
peerInfo @1 :PeerInfo; # peer info of the signal sender for reverse connect attempt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,8 +217,10 @@ struct ProtocolSet {
|
|||||||
struct NodeInfo {
|
struct NodeInfo {
|
||||||
networkClass @0 :NetworkClass; # network class of this node
|
networkClass @0 :NetworkClass; # network class of this node
|
||||||
outboundProtocols @1 :ProtocolSet; # protocols that can go outbound
|
outboundProtocols @1 :ProtocolSet; # protocols that can go outbound
|
||||||
dialInfoDetailList @2 :List(DialInfoDetail); # inbound dial info details for this node
|
minVersion @2 :UInt8; # minimum protocol version for rpc
|
||||||
relayPeerInfo @3 :PeerInfo; # (optional) relay peer info for this node
|
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 {
|
struct SignedNodeInfo {
|
||||||
@ -238,13 +240,15 @@ struct OperationStatusA {
|
|||||||
|
|
||||||
struct OperationValidateDialInfo {
|
struct OperationValidateDialInfo {
|
||||||
dialInfo @0 :DialInfo; # dial info to use for the receipt
|
dialInfo @0 :DialInfo; # dial info to use for the receipt
|
||||||
receipt @1 :Data; # receipt to return to dial info to prove it is reachable
|
minVersion @1 :UInt8; # minimum version for the direct receipt
|
||||||
redirect @2 :Bool; # request a different node do the validate
|
maxVersion @2 :UInt8; # maximum version for the direct receipt
|
||||||
alternatePort @3 :Bool; # return receipt from a different source port than the default
|
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 {
|
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 {
|
struct OperationFindNodeQ {
|
||||||
|
@ -78,8 +78,8 @@ impl Envelope {
|
|||||||
|
|
||||||
pub fn from_signed_data(data: &[u8]) -> Result<Envelope, ()> {
|
pub fn from_signed_data(data: &[u8]) -> Result<Envelope, ()> {
|
||||||
// Ensure we are at least the length of the 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 {
|
if data.len() < MIN_ENVELOPE_SIZE {
|
||||||
trace!("envelope too small: len={}", data.len());
|
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ use data_encoding::BASE64URL_NOPAD;
|
|||||||
// reserved: u8, // 0x05: Reserved for future use
|
// 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.
|
// 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.
|
// 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)
|
// 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
|
// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire receipt including header and extra data is appended to the packet
|
||||||
// }
|
// }
|
||||||
|
@ -534,11 +534,17 @@ impl Network {
|
|||||||
|
|
||||||
pub async fn tick(&self) -> Result<(), String> {
|
pub async fn tick(&self) -> Result<(), String> {
|
||||||
let network_class = self.get_network_class().unwrap_or(NetworkClass::Invalid);
|
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 we need to figure out our network class, tick the task for it
|
||||||
if network_class == NetworkClass::Invalid {
|
if network_class == NetworkClass::Invalid {
|
||||||
|
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?;
|
self.unlocked_inner.update_network_class_task.tick().await?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,10 @@ struct DiscoveryContextInner {
|
|||||||
intf_addrs: Option<Vec<SocketAddress>>,
|
intf_addrs: Option<Vec<SocketAddress>>,
|
||||||
protocol_type: Option<ProtocolType>,
|
protocol_type: Option<ProtocolType>,
|
||||||
address_type: Option<AddressType>,
|
address_type: Option<AddressType>,
|
||||||
external1_dial_info: Option<DialInfo>,
|
// first node contacted
|
||||||
external1: Option<SocketAddress>,
|
external_1_dial_info: Option<DialInfo>,
|
||||||
node_b: Option<NodeRef>,
|
external_1_address: Option<SocketAddress>,
|
||||||
|
node_1: Option<NodeRef>,
|
||||||
// detected public dialinfo
|
// detected public dialinfo
|
||||||
detected_network_class: Option<NetworkClass>,
|
detected_network_class: Option<NetworkClass>,
|
||||||
detected_public_dial_info: Option<DetectedPublicDialInfo>,
|
detected_public_dial_info: Option<DetectedPublicDialInfo>,
|
||||||
@ -40,9 +41,9 @@ impl DiscoveryContext {
|
|||||||
intf_addrs: None,
|
intf_addrs: None,
|
||||||
protocol_type: None,
|
protocol_type: None,
|
||||||
address_type: None,
|
address_type: None,
|
||||||
external1_dial_info: None,
|
external_1_dial_info: None,
|
||||||
external1: None,
|
external_1_address: None,
|
||||||
node_b: None,
|
node_1: None,
|
||||||
detected_network_class: None,
|
detected_network_class: None,
|
||||||
detected_public_dial_info: None,
|
detected_public_dial_info: None,
|
||||||
})),
|
})),
|
||||||
@ -64,6 +65,7 @@ impl DiscoveryContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ask for a public address check from a particular noderef
|
// 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> {
|
async fn request_public_address(&self, node_ref: NodeRef) -> Option<SocketAddress> {
|
||||||
let rpc = self.routing_table.rpc_processor();
|
let rpc = self.routing_table.rpc_processor();
|
||||||
rpc.rpc_call_status(node_ref.clone())
|
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
|
// 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(
|
async fn discover_external_address(
|
||||||
&self,
|
&self,
|
||||||
protocol_type: ProtocolType,
|
protocol_type: ProtocolType,
|
||||||
@ -109,6 +112,7 @@ impl DiscoveryContext {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This pulls the already-detected local interface dial info from the routing table
|
||||||
fn get_local_addresses(
|
fn get_local_addresses(
|
||||||
&self,
|
&self,
|
||||||
protocol_type: ProtocolType,
|
protocol_type: ProtocolType,
|
||||||
@ -135,10 +139,9 @@ impl DiscoveryContext {
|
|||||||
node_ref: NodeRef,
|
node_ref: NodeRef,
|
||||||
dial_info: DialInfo,
|
dial_info: DialInfo,
|
||||||
redirect: bool,
|
redirect: bool,
|
||||||
alternate_port: bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let rpc = self.routing_table.rpc_processor();
|
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
|
.await
|
||||||
.map_err(logthru_net!(
|
.map_err(logthru_net!(
|
||||||
"failed to send validate_dial_info to {:?}",
|
"failed to send validate_dial_info to {:?}",
|
||||||
@ -179,19 +182,20 @@ impl DiscoveryContext {
|
|||||||
inner.intf_addrs = Some(intf_addrs);
|
inner.intf_addrs = Some(intf_addrs);
|
||||||
inner.protocol_type = Some(protocol_type);
|
inner.protocol_type = Some(protocol_type);
|
||||||
inner.address_type = Some(address_type);
|
inner.address_type = Some(address_type);
|
||||||
inner.external1_dial_info = None;
|
inner.external_1_dial_info = None;
|
||||||
inner.external1 = None;
|
inner.external_1_address = None;
|
||||||
inner.node_b = 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 {
|
pub async fn protocol_get_external_address_1(&self) -> bool {
|
||||||
let (protocol_type, address_type) = {
|
let (protocol_type, address_type) = {
|
||||||
let inner = self.inner.lock();
|
let inner = self.inner.lock();
|
||||||
(inner.protocol_type.unwrap(), inner.address_type.unwrap())
|
(inner.protocol_type.unwrap(), inner.address_type.unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get our external address from some fast node, call it node B
|
// Get our external address from some fast node, call it node 1
|
||||||
let (external1, node_b) = match self
|
let (external_1, node_1) = match self
|
||||||
.discover_external_address(protocol_type, address_type, None)
|
.discover_external_address(protocol_type, address_type, None)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -202,54 +206,56 @@ impl DiscoveryContext {
|
|||||||
}
|
}
|
||||||
Some(v) => v,
|
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();
|
let mut inner = self.inner.lock();
|
||||||
inner.external1_dial_info = Some(external1_dial_info);
|
inner.external_1_dial_info = Some(external_1_dial_info);
|
||||||
inner.external1 = Some(external1);
|
inner.external_1_address = Some(external_1);
|
||||||
inner.node_b = Some(node_b);
|
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
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we know we are not behind NAT, check our firewall status
|
||||||
pub async fn protocol_process_no_nat(&self) -> Result<(), String> {
|
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();
|
let inner = self.inner.lock();
|
||||||
(
|
(
|
||||||
inner.node_b.as_ref().unwrap().clone(),
|
inner.node_1.as_ref().unwrap().clone(),
|
||||||
inner.external1_dial_info.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
|
// Do a validate_dial_info on the external address from a redirected node
|
||||||
if self
|
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
|
.await
|
||||||
{
|
{
|
||||||
// Add public dial info with Direct dialinfo class
|
// 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 {
|
else if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
||||||
// Got a port mapping, let's use it
|
// Got a port mapping, let's use it
|
||||||
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
||||||
} else {
|
} else {
|
||||||
// Add public dial info with Blocked dialinfo class
|
// 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);
|
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we know we are behind NAT check what kind
|
||||||
pub async fn protocol_process_nat(&self) -> Result<bool, String> {
|
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();
|
let inner = self.inner.lock();
|
||||||
(
|
(
|
||||||
inner.node_b.as_ref().unwrap().clone(),
|
inner.node_1.as_ref().unwrap().clone(),
|
||||||
inner.external1_dial_info.as_ref().unwrap().clone(),
|
inner.external_1_dial_info.as_ref().unwrap().clone(),
|
||||||
inner.external1.unwrap(),
|
inner.external_1_address.unwrap(),
|
||||||
inner.protocol_type.unwrap(),
|
inner.protocol_type.unwrap(),
|
||||||
inner.address_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
|
// 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
|
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
|
.await
|
||||||
{
|
{
|
||||||
// Yes, another machine can use the dial info directly, so Full Cone
|
// Yes, another machine can use the dial info directly, so Full Cone
|
||||||
// Add public dial info with full cone NAT network class
|
// 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);
|
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||||
|
|
||||||
// No more retries
|
// No more retries
|
||||||
@ -283,9 +289,9 @@ impl DiscoveryContext {
|
|||||||
|
|
||||||
// No, we are restricted, determine what kind of restriction
|
// 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
|
// Get our external address from some fast node, that is not node 1, call it node 2
|
||||||
let (external2, node_d) = match self
|
let (external_2_address, node_2) = match self
|
||||||
.discover_external_address(protocol_type, address_type, Some(node_b.node_id()))
|
.discover_external_address(protocol_type, address_type, Some(node_1.node_id()))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
None => {
|
None => {
|
||||||
@ -296,7 +302,7 @@ impl DiscoveryContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If we have two different external addresses, then this is a symmetric NAT
|
// 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
|
// Symmetric NAT is outbound only, no public dial info will work
|
||||||
self.set_detected_network_class(NetworkClass::OutboundOnly);
|
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
|
// 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
|
// 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
|
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
|
.await
|
||||||
{
|
{
|
||||||
// Got a reply from a non-default port, which means we're only address restricted
|
// Got a reply from a non-default port, which means we're only address restricted
|
||||||
self.set_detected_public_dial_info(
|
self.set_detected_public_dial_info(
|
||||||
external1_dial_info,
|
external_1_dial_info,
|
||||||
DialInfoClass::AddressRestrictedNAT,
|
DialInfoClass::AddressRestrictedNAT,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Didn't get a reply from a non-default port, which means we are also port restricted
|
// Didn't get a reply from a non-default port, which means we are also port restricted
|
||||||
self.set_detected_public_dial_info(
|
self.set_detected_public_dial_info(
|
||||||
external1_dial_info,
|
external_1_dial_info,
|
||||||
DialInfoClass::PortRestrictedNAT,
|
DialInfoClass::PortRestrictedNAT,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -348,13 +353,13 @@ impl Network {
|
|||||||
|
|
||||||
// Loop for restricted NAT retries
|
// Loop for restricted NAT retries
|
||||||
loop {
|
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 !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
|
// If we couldn't get an external address, then we should just try the whole network class detection again later
|
||||||
return Ok(());
|
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 res = {
|
||||||
let inner = context.inner.lock();
|
let inner = context.inner.lock();
|
||||||
@ -362,7 +367,7 @@ impl Network {
|
|||||||
.intf_addrs
|
.intf_addrs
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.contains(inner.external1.as_ref().unwrap())
|
.contains(inner.external_1_address.as_ref().unwrap())
|
||||||
};
|
};
|
||||||
if res {
|
if res {
|
||||||
// No NAT
|
// No NAT
|
||||||
@ -397,25 +402,25 @@ impl Network {
|
|||||||
// Start doing ipv6 protocol
|
// Start doing ipv6 protocol
|
||||||
context.protocol_begin(protocol_type, AddressType::IPV6);
|
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 !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
|
// If we couldn't get an external address, then we should just try the whole network class detection again later
|
||||||
return Ok(());
|
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();
|
let inner = context.inner.lock();
|
||||||
if !inner
|
if !inner
|
||||||
.intf_addrs
|
.intf_addrs
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.contains(inner.external1.as_ref().unwrap())
|
.contains(inner.external_1_address.as_ref().unwrap())
|
||||||
{
|
{
|
||||||
// IPv6 NAT is not supported today
|
// IPv6 NAT is not supported today
|
||||||
log_net!(warn
|
log_net!(warn
|
||||||
"IPv6 NAT is not supported for external address: {}",
|
"IPv6 NAT is not supported for external address: {}",
|
||||||
inner.external1.unwrap()
|
inner.external_1_address.unwrap()
|
||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -454,90 +459,90 @@ impl Network {
|
|||||||
.boxed(),
|
.boxed(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// UDPv6
|
// // UDPv6
|
||||||
unord.push(
|
// unord.push(
|
||||||
async {
|
// async {
|
||||||
let udpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
// let udpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||||
if let Err(e) = self
|
// if let Err(e) = self
|
||||||
.update_ipv6_protocol_dialinfo(&udpv6_context, ProtocolType::UDP)
|
// .update_ipv6_protocol_dialinfo(&udpv6_context, ProtocolType::UDP)
|
||||||
.await
|
// .await
|
||||||
{
|
// {
|
||||||
log_net!(debug "Failed UDPv6 dialinfo discovery: {}", e);
|
// log_net!(debug "Failed UDPv6 dialinfo discovery: {}", e);
|
||||||
return None;
|
// return None;
|
||||||
}
|
// }
|
||||||
Some(udpv6_context)
|
// Some(udpv6_context)
|
||||||
}
|
// }
|
||||||
.boxed(),
|
// .boxed(),
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
if protocol_config.inbound.contains(ProtocolType::TCP) {
|
// if protocol_config.inbound.contains(ProtocolType::TCP) {
|
||||||
// TCPv4
|
// // TCPv4
|
||||||
unord.push(
|
// unord.push(
|
||||||
async {
|
// async {
|
||||||
let tcpv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
// let tcpv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||||
if let Err(e) = self
|
// if let Err(e) = self
|
||||||
.update_ipv4_protocol_dialinfo(&tcpv4_context, ProtocolType::TCP)
|
// .update_ipv4_protocol_dialinfo(&tcpv4_context, ProtocolType::TCP)
|
||||||
.await
|
// .await
|
||||||
{
|
// {
|
||||||
log_net!(debug "Failed TCPv4 dialinfo discovery: {}", e);
|
// log_net!(debug "Failed TCPv4 dialinfo discovery: {}", e);
|
||||||
return None;
|
// return None;
|
||||||
}
|
// }
|
||||||
Some(tcpv4_context)
|
// Some(tcpv4_context)
|
||||||
}
|
// }
|
||||||
.boxed(),
|
// .boxed(),
|
||||||
);
|
// );
|
||||||
|
|
||||||
// TCPv6
|
// // TCPv6
|
||||||
unord.push(
|
// unord.push(
|
||||||
async {
|
// async {
|
||||||
let tcpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
// let tcpv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||||
if let Err(e) = self
|
// if let Err(e) = self
|
||||||
.update_ipv6_protocol_dialinfo(&tcpv6_context, ProtocolType::TCP)
|
// .update_ipv6_protocol_dialinfo(&tcpv6_context, ProtocolType::TCP)
|
||||||
.await
|
// .await
|
||||||
{
|
// {
|
||||||
log_net!(debug "Failed TCPv6 dialinfo discovery: {}", e);
|
// log_net!(debug "Failed TCPv6 dialinfo discovery: {}", e);
|
||||||
return None;
|
// return None;
|
||||||
}
|
// }
|
||||||
Some(tcpv6_context)
|
// Some(tcpv6_context)
|
||||||
}
|
// }
|
||||||
.boxed(),
|
// .boxed(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
if protocol_config.inbound.contains(ProtocolType::WS) {
|
// if protocol_config.inbound.contains(ProtocolType::WS) {
|
||||||
// WS4
|
// // WS4
|
||||||
unord.push(
|
// unord.push(
|
||||||
async {
|
// async {
|
||||||
let wsv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
// let wsv4_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||||
if let Err(e) = self
|
// if let Err(e) = self
|
||||||
.update_ipv4_protocol_dialinfo(&wsv4_context, ProtocolType::WS)
|
// .update_ipv4_protocol_dialinfo(&wsv4_context, ProtocolType::WS)
|
||||||
.await
|
// .await
|
||||||
{
|
// {
|
||||||
log_net!(debug "Failed WSv4 dialinfo discovery: {}", e);
|
// log_net!(debug "Failed WSv4 dialinfo discovery: {}", e);
|
||||||
return None;
|
// return None;
|
||||||
}
|
// }
|
||||||
Some(wsv4_context)
|
// Some(wsv4_context)
|
||||||
}
|
// }
|
||||||
.boxed(),
|
// .boxed(),
|
||||||
);
|
// );
|
||||||
|
|
||||||
// WSv6
|
// // WSv6
|
||||||
unord.push(
|
// unord.push(
|
||||||
async {
|
// async {
|
||||||
let wsv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
// let wsv6_context = DiscoveryContext::new(self.routing_table(), self.clone());
|
||||||
if let Err(e) = self
|
// if let Err(e) = self
|
||||||
.update_ipv6_protocol_dialinfo(&wsv6_context, ProtocolType::TCP)
|
// .update_ipv6_protocol_dialinfo(&wsv6_context, ProtocolType::TCP)
|
||||||
.await
|
// .await
|
||||||
{
|
// {
|
||||||
log_net!(debug "Failed WSv6 dialinfo discovery: {}", e);
|
// log_net!(debug "Failed WSv6 dialinfo discovery: {}", e);
|
||||||
return None;
|
// return None;
|
||||||
}
|
// }
|
||||||
Some(wsv6_context)
|
// Some(wsv6_context)
|
||||||
}
|
// }
|
||||||
.boxed(),
|
// .boxed(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Wait for all discovery futures to complete and collect contexts
|
// Wait for all discovery futures to complete and collect contexts
|
||||||
let mut contexts = Vec::<DiscoveryContext>::new();
|
let mut contexts = Vec::<DiscoveryContext>::new();
|
||||||
|
@ -112,10 +112,12 @@ impl WebsocketProtocolHandler {
|
|||||||
dial_info,
|
dial_info,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Make the real connection
|
||||||
let conn = Self::connect(None, dial_info)
|
let conn = Self::connect(None, dial_info)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("failed to connect websocket for unbound message: {}", e))?;
|
.map_err(|e| format!("failed to connect websocket for unbound message: {}", e))?;
|
||||||
|
|
||||||
conn.send(data).await
|
conn.send(data).await
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -421,96 +421,80 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates an out-of-band receipt
|
// Generates a multi-shot/normal receipt
|
||||||
pub fn generate_receipt<D: AsRef<[u8]>>(
|
pub fn generate_receipt(
|
||||||
&self,
|
&self,
|
||||||
expiration_us: u64,
|
expiration_us: u64,
|
||||||
expected_returns: u32,
|
expected_returns: u32,
|
||||||
extra_data: D,
|
|
||||||
callback: impl ReceiptCallback,
|
callback: impl ReceiptCallback,
|
||||||
) -> Result<Vec<u8>, String> {
|
) -> Result<ReceiptNonce, String> {
|
||||||
let receipt_manager = self.receipt_manager();
|
let receipt_manager = self.receipt_manager();
|
||||||
let routing_table = self.routing_table();
|
|
||||||
|
|
||||||
// Generate receipt and serialized form to return
|
// Generate receipt nonce
|
||||||
let nonce = Crypto::get_random_nonce();
|
let receipt_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())?;
|
|
||||||
|
|
||||||
// Record the receipt for later
|
// Record the receipt for later
|
||||||
let exp_ts = intf::get_timestamp() + expiration_us;
|
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,
|
&self,
|
||||||
expiration_us: u64,
|
expiration_us: u64,
|
||||||
extra_data: D,
|
) -> Result<(ReceiptNonce, EventualValueFuture<ReceiptEvent>), String> {
|
||||||
) -> Result<(Vec<u8>, EventualValueFuture<ReceiptEvent>), String> {
|
|
||||||
let receipt_manager = self.receipt_manager();
|
let receipt_manager = self.receipt_manager();
|
||||||
let routing_table = self.routing_table();
|
|
||||||
|
|
||||||
// Generate receipt and serialized form to return
|
// Generate receipt nonce
|
||||||
let nonce = Crypto::get_random_nonce();
|
let receipt_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())?;
|
|
||||||
|
|
||||||
// Record the receipt for later
|
// Record the receipt for later
|
||||||
let exp_ts = intf::get_timestamp() + expiration_us;
|
let exp_ts = intf::get_timestamp() + expiration_us;
|
||||||
let eventual = SingleShotEventual::new(Some(ReceiptEvent::Cancelled));
|
let eventual = SingleShotEventual::new(Some(ReceiptEvent::Cancelled));
|
||||||
let instance = eventual.instance();
|
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
|
// Process a received out-of-band receipt
|
||||||
pub async fn handle_out_of_band_receipt<R: AsRef<[u8]>>(
|
pub async fn handle_out_of_band_receipt<R: AsRef<[u8]>>(
|
||||||
&self,
|
&self,
|
||||||
receipt_data: R,
|
receipt_data: R,
|
||||||
descriptor: ConnectionDescriptor,
|
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let routing_table = self.routing_table();
|
|
||||||
let receipt_manager = self.receipt_manager();
|
let receipt_manager = self.receipt_manager();
|
||||||
let ts = intf::get_timestamp();
|
|
||||||
|
|
||||||
let receipt = Receipt::from_signed_data(receipt_data.as_ref())
|
let receipt = Receipt::from_signed_data(receipt_data.as_ref())
|
||||||
.map_err(|_| "failed to parse signed receipt".to_owned())?;
|
.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
|
receipt_manager
|
||||||
.handle_receipt(source_noderef, receipt)
|
.handle_receipt(receipt.get_nonce(), receipt.get_extra_data().to_vec(), None)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process a received in-band receipt
|
// Process a received in-band receipt
|
||||||
pub async fn handle_in_band_receipt<R: AsRef<[u8]>>(
|
pub async fn handle_in_band_receipt(
|
||||||
&self,
|
&self,
|
||||||
receipt_data: R,
|
receipt_nonce: ReceiptNonce,
|
||||||
|
extra_data: Vec<u8>,
|
||||||
inbound_nr: NodeRef,
|
inbound_nr: NodeRef,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let receipt_manager = self.receipt_manager();
|
let receipt_manager = self.receipt_manager();
|
||||||
|
|
||||||
let receipt = Receipt::from_signed_data(receipt_data.as_ref())
|
receipt_manager
|
||||||
.map_err(|_| "failed to parse signed receipt".to_owned())?;
|
.handle_receipt(receipt_nonce, extra_data, Some(inbound_nr))
|
||||||
|
.await
|
||||||
receipt_manager.handle_receipt(inbound_nr, receipt).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process a received signal
|
// Process a received signal
|
||||||
pub async fn handle_signal(&self, signal_info: SignalInfo) -> Result<(), String> {
|
pub async fn handle_signal(&self, signal_info: SignalInfo) -> Result<(), String> {
|
||||||
match signal_info {
|
match signal_info {
|
||||||
SignalInfo::ReverseConnect { receipt, peer_info } => {
|
SignalInfo::ReverseConnect {
|
||||||
|
receipt_nonce,
|
||||||
|
peer_info,
|
||||||
|
} => {
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let rpc = self.rpc_processor();
|
let rpc = self.rpc_processor();
|
||||||
|
|
||||||
@ -521,12 +505,16 @@ impl NetworkManager {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Make a reverse connection to the peer and send the receipt to it
|
// 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
|
.await
|
||||||
.map_err(map_to_string)?;
|
.map_err(map_to_string)?;
|
||||||
}
|
}
|
||||||
SignalInfo::HolePunch { receipt, peer_info } => {
|
SignalInfo::HolePunch {
|
||||||
|
receipt_nonce,
|
||||||
|
peer_info,
|
||||||
|
} => {
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
|
let rpc = self.rpc_processor();
|
||||||
|
|
||||||
// Add the peer info to our routing table
|
// Add the peer info to our routing table
|
||||||
let mut peer_nr = routing_table.register_node_with_signed_node_info(
|
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))
|
.first_filtered_dial_info_detail(Some(RoutingDomain::PublicInternet))
|
||||||
.ok_or_else(|| "No hole punch capable dialinfo found for node".to_owned())?;
|
.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
|
// 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
|
// Both sides will do this and then the receipt will get sent over the punched hole
|
||||||
self.net()
|
self.net()
|
||||||
@ -551,8 +545,8 @@ impl NetworkManager {
|
|||||||
|
|
||||||
// XXX: do we need a delay here? or another hole punch packet?
|
// 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
|
// Return the receipt using the same dial info send the receipt to it
|
||||||
self.send_direct_receipt(hole_punch_dial_info_detail.dial_info, receipt, false)
|
rpc.rpc_call_return_receipt(Destination::Direct(peer_nr), None, receipt_nonce, [])
|
||||||
.await
|
.await
|
||||||
.map_err(map_to_string)?;
|
.map_err(map_to_string)?;
|
||||||
}
|
}
|
||||||
@ -639,26 +633,26 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called by the RPC handler when we want to issue an direct receipt
|
// 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,
|
&self,
|
||||||
dial_info: DialInfo,
|
dial_info: DialInfo,
|
||||||
rcpt_data: B,
|
version: u8,
|
||||||
alternate_port: bool,
|
receipt_nonce: Nonce,
|
||||||
|
extra_data: D,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Validate receipt before we send it, otherwise this may be arbitrary data!
|
let routing_table = self.routing_table();
|
||||||
let _ = Receipt::from_signed_data(rcpt_data.as_ref())
|
let node_id = routing_table.node_id();
|
||||||
.map_err(|_| "failed to validate direct receipt".to_owned())?;
|
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
|
// Send receipt directly
|
||||||
if alternate_port {
|
|
||||||
self.net()
|
self.net()
|
||||||
.send_data_unbound_to_dial_info(dial_info, rcpt_data.as_ref().to_vec())
|
.send_data_unbound_to_dial_info(dial_info, rcpt_data)
|
||||||
.await
|
.await
|
||||||
} else {
|
|
||||||
self.net()
|
|
||||||
.send_data_to_dial_info(dial_info, rcpt_data.as_ref().to_vec())
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out how to reach a node
|
// Figure out how to reach a node
|
||||||
@ -790,8 +784,8 @@ impl NetworkManager {
|
|||||||
// Build a return receipt for the signal
|
// Build a return receipt for the signal
|
||||||
let receipt_timeout =
|
let receipt_timeout =
|
||||||
ms_to_us(self.config.get().network.reverse_connection_receipt_time_ms);
|
ms_to_us(self.config.get().network.reverse_connection_receipt_time_ms);
|
||||||
let (receipt, eventual_value) = self
|
let (receipt_nonce, eventual_value) = self
|
||||||
.generate_single_shot_receipt(receipt_timeout, [])
|
.generate_single_shot_receipt(receipt_timeout)
|
||||||
.map_err(map_to_string)?;
|
.map_err(map_to_string)?;
|
||||||
|
|
||||||
// Get our peer info
|
// Get our peer info
|
||||||
@ -802,14 +796,23 @@ impl NetworkManager {
|
|||||||
rpc.rpc_call_signal(
|
rpc.rpc_call_signal(
|
||||||
Destination::Relay(relay_nr.clone(), target_nr.node_id()),
|
Destination::Relay(relay_nr.clone(), target_nr.node_id()),
|
||||||
None,
|
None,
|
||||||
SignalInfo::ReverseConnect { receipt, peer_info },
|
SignalInfo::ReverseConnect {
|
||||||
|
receipt_nonce,
|
||||||
|
peer_info,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(logthru_net!("failed to send signal to {:?}", relay_nr))
|
.map_err(logthru_net!("failed to send signal to {:?}", relay_nr))
|
||||||
.map_err(map_to_string)?;
|
.map_err(map_to_string)?;
|
||||||
// Wait for the return receipt
|
// Wait for the return receipt
|
||||||
let inbound_nr = match eventual_value.await.take_value().unwrap() {
|
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 => {
|
ReceiptEvent::Expired => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"reverse connect receipt expired from {:?}",
|
"reverse connect receipt expired from {:?}",
|
||||||
@ -867,8 +870,8 @@ impl NetworkManager {
|
|||||||
// Build a return receipt for the signal
|
// Build a return receipt for the signal
|
||||||
let receipt_timeout =
|
let receipt_timeout =
|
||||||
ms_to_us(self.config.get().network.reverse_connection_receipt_time_ms);
|
ms_to_us(self.config.get().network.reverse_connection_receipt_time_ms);
|
||||||
let (receipt, eventual_value) = self
|
let (receipt_nonce, eventual_value) = self
|
||||||
.generate_single_shot_receipt(receipt_timeout, [])
|
.generate_single_shot_receipt(receipt_timeout)
|
||||||
.map_err(map_to_string)?;
|
.map_err(map_to_string)?;
|
||||||
|
|
||||||
// Get our peer info
|
// Get our peer info
|
||||||
@ -890,7 +893,10 @@ impl NetworkManager {
|
|||||||
rpc.rpc_call_signal(
|
rpc.rpc_call_signal(
|
||||||
Destination::Relay(relay_nr.clone(), target_nr.node_id()),
|
Destination::Relay(relay_nr.clone(), target_nr.node_id()),
|
||||||
None,
|
None,
|
||||||
SignalInfo::HolePunch { receipt, peer_info },
|
SignalInfo::HolePunch {
|
||||||
|
receipt_nonce,
|
||||||
|
peer_info,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(logthru_net!("failed to send signal to {:?}", relay_nr))
|
.map_err(logthru_net!("failed to send signal to {:?}", relay_nr))
|
||||||
@ -898,19 +904,28 @@ impl NetworkManager {
|
|||||||
|
|
||||||
// Wait for the return receipt
|
// Wait for the return receipt
|
||||||
let inbound_nr = match eventual_value.await.take_value().unwrap() {
|
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 => {
|
ReceiptEvent::Expired => {
|
||||||
return Err(format!("hole punch receipt expired from {:?}", target_nr));
|
return Err(format!("hole punch receipt expired from {}", target_nr));
|
||||||
}
|
}
|
||||||
ReceiptEvent::Cancelled => {
|
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
|
// 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 they aren't the same, we should error on this and figure out what then hell is up
|
||||||
if target_nr != inbound_nr {
|
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
|
// And now use the existing connection to send over
|
||||||
@ -1020,9 +1035,14 @@ impl NetworkManager {
|
|||||||
// Network accounting
|
// Network accounting
|
||||||
self.stats_packet_rcvd(descriptor.remote.to_socket_addr().ip(), data.len() as u64);
|
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?
|
// Is this an out-of-band receipt instead of an envelope?
|
||||||
if data[0..4] == *RECEIPT_MAGIC {
|
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);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,13 @@ use xx::*;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ReceiptEvent {
|
pub enum ReceiptEvent {
|
||||||
Returned(NodeRef),
|
ReturnedOutOfBand {
|
||||||
|
extra_data: Vec<u8>,
|
||||||
|
},
|
||||||
|
ReturnedInBand {
|
||||||
|
inbound_noderef: NodeRef,
|
||||||
|
extra_data: Vec<u8>,
|
||||||
|
},
|
||||||
Expired,
|
Expired,
|
||||||
Cancelled,
|
Cancelled,
|
||||||
}
|
}
|
||||||
@ -108,29 +114,29 @@ impl fmt::Debug for ReceiptRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ReceiptRecord {
|
impl ReceiptRecord {
|
||||||
pub fn from_receipt(
|
pub fn new(
|
||||||
receipt: &Receipt,
|
receipt_nonce: ReceiptNonce,
|
||||||
expiration_ts: u64,
|
expiration_ts: u64,
|
||||||
expected_returns: u32,
|
expected_returns: u32,
|
||||||
receipt_callback: impl ReceiptCallback,
|
receipt_callback: impl ReceiptCallback,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
expiration_ts,
|
expiration_ts,
|
||||||
nonce: receipt.get_nonce(),
|
nonce: receipt_nonce,
|
||||||
expected_returns,
|
expected_returns,
|
||||||
returns_so_far: 0u32,
|
returns_so_far: 0u32,
|
||||||
receipt_callback: ReceiptRecordCallbackType::Normal(Box::new(receipt_callback)),
|
receipt_callback: ReceiptRecordCallbackType::Normal(Box::new(receipt_callback)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_single_shot_receipt(
|
pub fn new_single_shot(
|
||||||
receipt: &Receipt,
|
receipt_nonce: ReceiptNonce,
|
||||||
expiration_ts: u64,
|
expiration_ts: u64,
|
||||||
eventual: ReceiptSingleShotType,
|
eventual: ReceiptSingleShotType,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
expiration_ts,
|
expiration_ts,
|
||||||
nonce: receipt.get_nonce(),
|
nonce: receipt_nonce,
|
||||||
returns_so_far: 0u32,
|
returns_so_far: 0u32,
|
||||||
expected_returns: 1u32,
|
expected_returns: 1u32,
|
||||||
receipt_callback: ReceiptRecordCallbackType::SingleShot(Some(eventual)),
|
receipt_callback: ReceiptRecordCallbackType::SingleShot(Some(eventual)),
|
||||||
@ -167,7 +173,7 @@ impl PartialOrd for ReceiptRecordTimestampSort {
|
|||||||
|
|
||||||
pub struct ReceiptManagerInner {
|
pub struct ReceiptManagerInner {
|
||||||
network_manager: NetworkManager,
|
network_manager: NetworkManager,
|
||||||
receipts_by_nonce: BTreeMap<ReceiptNonce, Arc<Mutex<ReceiptRecord>>>,
|
records_by_nonce: BTreeMap<ReceiptNonce, Arc<Mutex<ReceiptRecord>>>,
|
||||||
next_oldest_ts: Option<u64>,
|
next_oldest_ts: Option<u64>,
|
||||||
timeout_task: SingleFuture<()>,
|
timeout_task: SingleFuture<()>,
|
||||||
}
|
}
|
||||||
@ -181,7 +187,7 @@ impl ReceiptManager {
|
|||||||
fn new_inner(network_manager: NetworkManager) -> ReceiptManagerInner {
|
fn new_inner(network_manager: NetworkManager) -> ReceiptManagerInner {
|
||||||
ReceiptManagerInner {
|
ReceiptManagerInner {
|
||||||
network_manager,
|
network_manager,
|
||||||
receipts_by_nonce: BTreeMap::new(),
|
records_by_nonce: BTreeMap::new(),
|
||||||
next_oldest_ts: None,
|
next_oldest_ts: None,
|
||||||
timeout_task: SingleFuture::new(),
|
timeout_task: SingleFuture::new(),
|
||||||
}
|
}
|
||||||
@ -240,7 +246,7 @@ impl ReceiptManager {
|
|||||||
{
|
{
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
let mut expired_nonces = Vec::new();
|
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();
|
let receipt_inner = v.lock();
|
||||||
if receipt_inner.expiration_ts <= now {
|
if receipt_inner.expiration_ts <= now {
|
||||||
// Expire this receipt
|
// Expire this receipt
|
||||||
@ -257,10 +263,7 @@ impl ReceiptManager {
|
|||||||
}
|
}
|
||||||
// Now remove the expired receipts
|
// Now remove the expired receipts
|
||||||
for e in expired_nonces {
|
for e in expired_nonces {
|
||||||
let expired_record = inner
|
let expired_record = inner.records_by_nonce.remove(&e).expect("key should exist");
|
||||||
.receipts_by_nonce
|
|
||||||
.remove(&e)
|
|
||||||
.expect("key should exist");
|
|
||||||
expired_records.push(expired_record);
|
expired_records.push(expired_record);
|
||||||
}
|
}
|
||||||
// Update the next oldest timestamp
|
// Update the next oldest timestamp
|
||||||
@ -305,37 +308,39 @@ impl ReceiptManager {
|
|||||||
|
|
||||||
pub fn record_receipt(
|
pub fn record_receipt(
|
||||||
&self,
|
&self,
|
||||||
receipt: Receipt,
|
receipt_nonce: ReceiptNonce,
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
expected_returns: u32,
|
expected_returns: u32,
|
||||||
callback: impl ReceiptCallback,
|
callback: impl ReceiptCallback,
|
||||||
) {
|
) {
|
||||||
log_rpc!(debug "== New Multiple Receipt ({}) {} ", expected_returns, receipt.get_nonce().encode());
|
log_rpc!(debug "== New Multiple Receipt ({}) {} ", expected_returns, receipt_nonce.encode());
|
||||||
let record = Arc::new(Mutex::new(ReceiptRecord::from_receipt(
|
let record = Arc::new(Mutex::new(ReceiptRecord::new(
|
||||||
&receipt,
|
receipt_nonce,
|
||||||
expiration,
|
expiration,
|
||||||
expected_returns,
|
expected_returns,
|
||||||
callback,
|
callback,
|
||||||
)));
|
)));
|
||||||
let mut inner = self.inner.lock();
|
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);
|
Self::update_next_oldest_timestamp(&mut *inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record_single_shot_receipt(
|
pub fn record_single_shot_receipt(
|
||||||
&self,
|
&self,
|
||||||
receipt: Receipt,
|
receipt_nonce: ReceiptNonce,
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
eventual: ReceiptSingleShotType,
|
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(
|
let record = Arc::new(Mutex::new(ReceiptRecord::new_single_shot(
|
||||||
&receipt, expiration, eventual,
|
receipt_nonce,
|
||||||
|
expiration,
|
||||||
|
eventual,
|
||||||
)));
|
)));
|
||||||
let mut inner = self.inner.lock();
|
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);
|
Self::update_next_oldest_timestamp(&mut *inner);
|
||||||
}
|
}
|
||||||
@ -343,7 +348,7 @@ impl ReceiptManager {
|
|||||||
fn update_next_oldest_timestamp(inner: &mut ReceiptManagerInner) {
|
fn update_next_oldest_timestamp(inner: &mut ReceiptManagerInner) {
|
||||||
// Update the next oldest timestamp
|
// Update the next oldest timestamp
|
||||||
let mut new_next_oldest_ts: Option<u64> = None;
|
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();
|
let receipt_inner = v.lock();
|
||||||
if new_next_oldest_ts.is_none()
|
if new_next_oldest_ts.is_none()
|
||||||
|| receipt_inner.expiration_ts < new_next_oldest_ts.unwrap()
|
|| receipt_inner.expiration_ts < new_next_oldest_ts.unwrap()
|
||||||
@ -362,7 +367,7 @@ impl ReceiptManager {
|
|||||||
// Remove the record
|
// Remove the record
|
||||||
let record = {
|
let record = {
|
||||||
let mut inner = self.inner.lock();
|
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,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
return Err("receipt not recorded".to_owned());
|
return Err("receipt not recorded".to_owned());
|
||||||
@ -386,14 +391,31 @@ impl ReceiptManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_receipt(&self, node_ref: NodeRef, receipt: Receipt) -> Result<(), String> {
|
pub async fn handle_receipt(
|
||||||
log_rpc!(debug "<<== RECEIPT {} <- {}", receipt.get_nonce().encode(), node_ref);
|
&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
|
// Increment return count
|
||||||
let callback_future = {
|
let callback_future = {
|
||||||
// Look up the receipt record from the nonce
|
// Look up the receipt record from the nonce
|
||||||
let mut inner = self.inner.lock();
|
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(),
|
Some(r) => r.clone(),
|
||||||
None => {
|
None => {
|
||||||
return Err("receipt not recorded".to_owned());
|
return Err("receipt not recorded".to_owned());
|
||||||
@ -402,12 +424,22 @@ impl ReceiptManager {
|
|||||||
// Generate the callback future
|
// Generate the callback future
|
||||||
let mut record_mut = record.lock();
|
let mut record_mut = record.lock();
|
||||||
record_mut.returns_so_far += 1;
|
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
|
// Remove the record if we're done
|
||||||
if record_mut.returns_so_far == record_mut.expected_returns {
|
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);
|
Self::update_next_oldest_timestamp(&mut *inner);
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,10 @@ impl BucketEntry {
|
|||||||
return;
|
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);
|
self.opt_signed_node_info = Some(signed_node_info);
|
||||||
}
|
}
|
||||||
pub fn update_local_node_info(&mut self, local_node_info: LocalNodeInfo) {
|
pub fn update_local_node_info(&mut self, local_node_info: LocalNodeInfo) {
|
||||||
|
@ -30,18 +30,41 @@ impl RoutingTable {
|
|||||||
if gdis.is_empty() {
|
if gdis.is_empty() {
|
||||||
out += "No TXT Record\n";
|
out += "No TXT Record\n";
|
||||||
} else {
|
} else {
|
||||||
out += "TXT Record:\n";
|
let mut short_urls = Vec::new();
|
||||||
out += &self.node_id().encode();
|
let mut some_hostname = Option::<String>::None;
|
||||||
|
|
||||||
let mut urls = Vec::new();
|
|
||||||
for gdi in gdis {
|
for gdi in gdis {
|
||||||
urls.push(gdi.dial_info.to_url().await);
|
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);
|
||||||
}
|
}
|
||||||
urls.sort();
|
|
||||||
urls.dedup();
|
|
||||||
|
|
||||||
for url in urls {
|
short_urls.push(short_url);
|
||||||
out += &format!(",{}", 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";
|
out += "\n";
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,8 @@ impl RoutingTable {
|
|||||||
NodeInfo {
|
NodeInfo {
|
||||||
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
|
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
|
||||||
outbound_protocols: netman.get_protocol_config().unwrap_or_default().outbound,
|
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),
|
dial_info_detail_list: self.dial_info_details(RoutingDomain::PublicInternet),
|
||||||
relay_peer_info: relay_node.and_then(|rn| rn.peer_info().map(Box::new)),
|
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;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BootstrapRecord {
|
||||||
|
min_version: u8,
|
||||||
|
max_version: u8,
|
||||||
|
dial_info_details: Vec<DialInfoDetail>,
|
||||||
|
}
|
||||||
|
pub type BootstrapRecordMap = BTreeMap<DHTKey, BootstrapRecord>;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||||
pub enum RoutingDomain {
|
pub enum RoutingDomain {
|
||||||
PublicInternet,
|
PublicInternet,
|
||||||
@ -817,9 +827,10 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap lookup process
|
// Bootstrap lookup process
|
||||||
async fn resolve_bootstrap(&self, bootstrap: Vec<String>) -> Result<Vec<NodeDialInfo>, String> {
|
async fn resolve_bootstrap(
|
||||||
let mut out = Vec::<NodeDialInfo>::new();
|
&self,
|
||||||
|
bootstrap: Vec<String>,
|
||||||
|
) -> Result<BootstrapRecordMap, String> {
|
||||||
// Resolve from bootstrap root to bootstrap hostnames
|
// Resolve from bootstrap root to bootstrap hostnames
|
||||||
let mut bsnames = Vec::<String>::new();
|
let mut bsnames = Vec::<String>::new();
|
||||||
for bh in bootstrap {
|
for bh in bootstrap {
|
||||||
@ -856,20 +867,64 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
};
|
};
|
||||||
// for each record resolve into node dial info strings
|
// for each record resolve into key/bootstraprecord pairs
|
||||||
let mut nodedialinfos: Vec<NodeDialInfo> = Vec::new();
|
let mut bootstrap_records: Vec<(DHTKey, BootstrapRecord)> = Vec::new();
|
||||||
for bsnirecord in bsnirecords {
|
for bsnirecord in bsnirecords {
|
||||||
// split bootstrap node record by commas. example:
|
// Bootstrap TXT Record Format Version 0:
|
||||||
// 7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ,tcp://bootstrap-dev-alpha.veilid.net:5150,udp://bootstrap-dev-alpha.veilid.net:5150,ws://bootstrap-dev-alpha.veilid.net:5150/ws
|
// txt_version,min_version,max_version,nodeid,hostname,dialinfoshort*
|
||||||
let mut records = bsnirecord.split(',').map(|x| x.trim());
|
//
|
||||||
let node_id_str = match records.next() {
|
// Split bootstrap node record by commas. Example:
|
||||||
Some(v) => v,
|
// 0,0,0,7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ,bootstrap-dev-alpha.veilid.net,T5150,U5150,W5150/ws
|
||||||
None => {
|
let records: Vec<String> = bsnirecord
|
||||||
warn!("no node id specified in bootstrap node txt record");
|
.trim()
|
||||||
|
.split(',')
|
||||||
|
.map(|x| x.trim().to_owned())
|
||||||
|
.collect();
|
||||||
|
if records.len() < 6 {
|
||||||
|
warn!("invalid number of fields in bootstrap txt record");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap TXT record version
|
||||||
|
let txt_version: u8 = match records[0].parse::<u8>() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"invalid txt_version specified in bootstrap node txt record: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Decode the node id
|
if txt_version != BOOTSTRAP_TXT_VERSION {
|
||||||
|
warn!("unsupported bootstrap txt record version");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min/Max wire protocol version
|
||||||
|
let min_version: u8 = match records[1].parse::<u8>() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"invalid min_version specified in bootstrap node txt record: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let max_version: u8 = match records[2].parse::<u8>() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"invalid max_version specified in bootstrap node txt record: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Node Id
|
||||||
|
let node_id_str = &records[3];
|
||||||
let node_id_key = match DHTKey::try_decode(node_id_str) {
|
let node_id_key = match DHTKey::try_decode(node_id_str) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -881,16 +936,23 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Hostname
|
||||||
|
let hostname_str = &records[4];
|
||||||
|
|
||||||
// If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node
|
// If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node
|
||||||
if self.node_id() == node_id_key {
|
if self.node_id() == node_id_key {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve each record and store in node dial infos list
|
// Resolve each record and store in node dial infos list
|
||||||
let node_id = NodeId::new(node_id_key);
|
let mut bootstrap_record = BootstrapRecord {
|
||||||
for rec in records {
|
min_version,
|
||||||
|
max_version,
|
||||||
|
dial_info_details: Vec::new(),
|
||||||
|
};
|
||||||
|
for rec in &records[5..] {
|
||||||
let rec = rec.trim();
|
let rec = rec.trim();
|
||||||
let dial_infos = match DialInfo::try_vec_from_url(rec) {
|
let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) {
|
||||||
Ok(dis) => dis,
|
Ok(dis) => dis,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Couldn't resolve bootstrap node dial info {}: {}", rec, e);
|
warn!("Couldn't resolve bootstrap node dial info {}: {}", rec, e);
|
||||||
@ -898,24 +960,34 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for dial_info in dial_infos {
|
for di in dial_infos {
|
||||||
nodedialinfos.push(NodeDialInfo {
|
bootstrap_record.dial_info_details.push(DialInfoDetail {
|
||||||
node_id: node_id.clone(),
|
dial_info: di,
|
||||||
dial_info,
|
class: DialInfoClass::Direct,
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(nodedialinfos)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
while let Some(ndis) = unord.next().await {
|
}
|
||||||
if let Some(mut ndis) = ndis {
|
bootstrap_records.push((node_id_key, bootstrap_record));
|
||||||
out.append(&mut ndis);
|
}
|
||||||
|
Some(bootstrap_records)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut bsmap = BootstrapRecordMap::new();
|
||||||
|
while let Some(bootstrap_records) = unord.next().await {
|
||||||
|
if let Some(bootstrap_records) = bootstrap_records {
|
||||||
|
for (bskey, mut bsrec) in bootstrap_records {
|
||||||
|
let rec = bsmap.entry(bskey).or_insert_with(|| BootstrapRecord {
|
||||||
|
min_version: bsrec.min_version,
|
||||||
|
max_version: bsrec.max_version,
|
||||||
|
dial_info_details: Vec::new(),
|
||||||
|
});
|
||||||
|
rec.dial_info_details.append(&mut bsrec.dial_info_details);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(out)
|
Ok(bsmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn bootstrap_task_routine(self) -> Result<(), String> {
|
async fn bootstrap_task_routine(self) -> Result<(), String> {
|
||||||
@ -930,8 +1002,10 @@ impl RoutingTable {
|
|||||||
log_rtab!(debug "--- bootstrap_task");
|
log_rtab!(debug "--- bootstrap_task");
|
||||||
|
|
||||||
// If we aren't specifying a bootstrap node list explicitly, then pull from the bootstrap server(s)
|
// If we aren't specifying a bootstrap node list explicitly, then pull from the bootstrap server(s)
|
||||||
let bootstrap_node_dial_infos = if !bootstrap_nodes.is_empty() {
|
|
||||||
let mut bsnvec = Vec::new();
|
let bsmap: BootstrapRecordMap = if !bootstrap_nodes.is_empty() {
|
||||||
|
let mut bsmap = BootstrapRecordMap::new();
|
||||||
|
let mut bootstrap_node_dial_infos = Vec::new();
|
||||||
for b in bootstrap_nodes {
|
for b in bootstrap_nodes {
|
||||||
let ndis = NodeDialInfo::from_str(b.as_str())
|
let ndis = NodeDialInfo::from_str(b.as_str())
|
||||||
.map_err(map_to_string)
|
.map_err(map_to_string)
|
||||||
@ -939,26 +1013,30 @@ impl RoutingTable {
|
|||||||
"Invalid node dial info in bootstrap entry: {}",
|
"Invalid node dial info in bootstrap entry: {}",
|
||||||
b
|
b
|
||||||
))?;
|
))?;
|
||||||
bsnvec.push(ndis);
|
bootstrap_node_dial_infos.push(ndis);
|
||||||
}
|
}
|
||||||
bsnvec
|
for ndi in bootstrap_node_dial_infos {
|
||||||
|
let node_id = ndi.node_id.key;
|
||||||
|
bsmap
|
||||||
|
.entry(node_id)
|
||||||
|
.or_insert_with(|| BootstrapRecord {
|
||||||
|
min_version: MIN_VERSION,
|
||||||
|
max_version: MAX_VERSION,
|
||||||
|
dial_info_details: Vec::new(),
|
||||||
|
})
|
||||||
|
.dial_info_details
|
||||||
|
.push(DialInfoDetail {
|
||||||
|
dial_info: ndi.dial_info,
|
||||||
|
class: DialInfoClass::Direct, // Bootstraps are always directly reachable
|
||||||
|
});
|
||||||
|
}
|
||||||
|
bsmap
|
||||||
} else {
|
} else {
|
||||||
// Resolve bootstrap servers and recurse their TXT entries
|
// Resolve bootstrap servers and recurse their TXT entries
|
||||||
self.resolve_bootstrap(bootstrap).await?
|
self.resolve_bootstrap(bootstrap).await?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map all bootstrap entries to a single key with multiple dialinfo
|
// Map all bootstrap entries to a single key with multiple dialinfo
|
||||||
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfoDetail>> = BTreeMap::new();
|
|
||||||
for ndi in bootstrap_node_dial_infos {
|
|
||||||
let node_id = ndi.node_id.key;
|
|
||||||
bsmap
|
|
||||||
.entry(node_id)
|
|
||||||
.or_insert_with(Vec::new)
|
|
||||||
.push(DialInfoDetail {
|
|
||||||
dial_info: ndi.dial_info,
|
|
||||||
class: DialInfoClass::Direct, // Bootstraps are always directly reachable
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run all bootstrap operations concurrently
|
// Run all bootstrap operations concurrently
|
||||||
let mut unord = FuturesUnordered::new();
|
let mut unord = FuturesUnordered::new();
|
||||||
@ -972,7 +1050,9 @@ impl RoutingTable {
|
|||||||
SignedNodeInfo::with_no_signature(NodeInfo {
|
SignedNodeInfo::with_no_signature(NodeInfo {
|
||||||
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
|
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
|
||||||
outbound_protocols: ProtocolSet::empty(), // Bootstraps do not participate in relaying and will not make outbound requests
|
outbound_protocols: ProtocolSet::empty(), // Bootstraps do not participate in relaying and will not make outbound requests
|
||||||
dial_info_detail_list: v, // Dial info is as specified in the bootstrap list
|
min_version: v.min_version, // Minimum protocol version specified in txt record
|
||||||
|
max_version: v.max_version, // Maximum protocol version specified in txt record
|
||||||
|
dial_info_detail_list: v.dial_info_details, // Dial info is as specified in the bootstrap list
|
||||||
relay_peer_info: None, // Bootstraps never require a relay themselves
|
relay_peer_info: None, // Bootstraps never require a relay themselves
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -228,6 +228,14 @@ impl NodeRef {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// Verify this connection matches the noderef filter
|
||||||
|
if let Some(filter) = &self.filter {
|
||||||
|
if !last_connection.matches_filter(filter) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Should we check the connection table?
|
// Should we check the connection table?
|
||||||
if last_connection.protocol_type().is_connection_oriented() {
|
if last_connection.protocol_type().is_connection_oriented() {
|
||||||
// Look the connection up in the connection manager and see if it's still there
|
// Look the connection up in the connection manager and see if it's still there
|
||||||
|
@ -10,6 +10,9 @@ pub fn encode_node_info(
|
|||||||
let mut ps_builder = builder.reborrow().init_outbound_protocols();
|
let mut ps_builder = builder.reborrow().init_outbound_protocols();
|
||||||
encode_protocol_set(&node_info.outbound_protocols, &mut ps_builder)?;
|
encode_protocol_set(&node_info.outbound_protocols, &mut ps_builder)?;
|
||||||
|
|
||||||
|
builder.set_min_version(node_info.min_version);
|
||||||
|
builder.set_max_version(node_info.max_version);
|
||||||
|
|
||||||
let mut didl_builder = builder.reborrow().init_dial_info_detail_list(
|
let mut didl_builder = builder.reborrow().init_dial_info_detail_list(
|
||||||
node_info
|
node_info
|
||||||
.dial_info_detail_list
|
.dial_info_detail_list
|
||||||
@ -51,6 +54,9 @@ pub fn decode_node_info(
|
|||||||
.map_err(map_error_capnp_error!())?,
|
.map_err(map_error_capnp_error!())?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let min_version = reader.reborrow().get_min_version();
|
||||||
|
let max_version = reader.reborrow().get_max_version();
|
||||||
|
|
||||||
let didl_reader = reader
|
let didl_reader = reader
|
||||||
.reborrow()
|
.reborrow()
|
||||||
.get_dial_info_detail_list()
|
.get_dial_info_detail_list()
|
||||||
@ -84,6 +90,8 @@ pub fn decode_node_info(
|
|||||||
Ok(NodeInfo {
|
Ok(NodeInfo {
|
||||||
network_class,
|
network_class,
|
||||||
outbound_protocols,
|
outbound_protocols,
|
||||||
|
min_version,
|
||||||
|
max_version,
|
||||||
dial_info_detail_list,
|
dial_info_detail_list,
|
||||||
relay_peer_info,
|
relay_peer_info,
|
||||||
})
|
})
|
||||||
|
@ -6,28 +6,24 @@ pub fn encode_signal_info(
|
|||||||
builder: &mut veilid_capnp::operation_signal::Builder,
|
builder: &mut veilid_capnp::operation_signal::Builder,
|
||||||
) -> Result<(), RPCError> {
|
) -> Result<(), RPCError> {
|
||||||
match signal_info {
|
match signal_info {
|
||||||
SignalInfo::HolePunch { receipt, peer_info } => {
|
SignalInfo::HolePunch {
|
||||||
|
receipt_nonce,
|
||||||
|
peer_info,
|
||||||
|
} => {
|
||||||
let mut hp_builder = builder.reborrow().init_hole_punch();
|
let mut hp_builder = builder.reborrow().init_hole_punch();
|
||||||
let rcpt_builder =
|
let mut rn_builder = hp_builder.reborrow().init_receipt_nonce();
|
||||||
hp_builder
|
encode_nonce(receipt_nonce, &mut rn_builder);
|
||||||
.reborrow()
|
|
||||||
.init_receipt(receipt.len().try_into().map_err(map_error_protocol!(
|
|
||||||
"invalid receipt length in hole punch signal info"
|
|
||||||
))?);
|
|
||||||
rcpt_builder.copy_from_slice(receipt.as_slice());
|
|
||||||
let mut pi_builder = hp_builder.init_peer_info();
|
let mut pi_builder = hp_builder.init_peer_info();
|
||||||
encode_peer_info(peer_info, &mut pi_builder)?;
|
encode_peer_info(peer_info, &mut pi_builder)?;
|
||||||
}
|
}
|
||||||
SignalInfo::ReverseConnect { receipt, peer_info } => {
|
SignalInfo::ReverseConnect {
|
||||||
let mut hp_builder = builder.reborrow().init_reverse_connect();
|
receipt_nonce,
|
||||||
let rcpt_builder =
|
peer_info,
|
||||||
hp_builder
|
} => {
|
||||||
.reborrow()
|
let mut rc_builder = builder.reborrow().init_reverse_connect();
|
||||||
.init_receipt(receipt.len().try_into().map_err(map_error_protocol!(
|
let mut rn_builder = rc_builder.reborrow().init_receipt_nonce();
|
||||||
"invalid receipt length in reverse connect signal info"
|
encode_nonce(receipt_nonce, &mut rn_builder);
|
||||||
))?);
|
let mut pi_builder = rc_builder.init_peer_info();
|
||||||
rcpt_builder.copy_from_slice(receipt.as_slice());
|
|
||||||
let mut pi_builder = hp_builder.init_peer_info();
|
|
||||||
encode_peer_info(peer_info, &mut pi_builder)?;
|
encode_peer_info(peer_info, &mut pi_builder)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,18 +45,17 @@ pub fn decode_signal_info(
|
|||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(_) => return Err(rpc_error_internal("invalid hole punch")),
|
Err(_) => return Err(rpc_error_internal("invalid hole punch")),
|
||||||
};
|
};
|
||||||
let receipt = r
|
let receipt_nonce =
|
||||||
.get_receipt()
|
decode_nonce(&r.get_receipt_nonce().map_err(map_error_capnp_error!())?);
|
||||||
.map_err(map_error_protocol!(
|
|
||||||
"invalid receipt in hole punch signal info"
|
|
||||||
))?
|
|
||||||
.to_vec();
|
|
||||||
let pi_reader = r.get_peer_info().map_err(map_error_protocol!(
|
let pi_reader = r.get_peer_info().map_err(map_error_protocol!(
|
||||||
"invalid peer info in hole punch signal info"
|
"invalid peer info in hole punch signal info"
|
||||||
))?;
|
))?;
|
||||||
let peer_info = decode_peer_info(&pi_reader, true)?;
|
let peer_info = decode_peer_info(&pi_reader, true)?;
|
||||||
|
|
||||||
SignalInfo::HolePunch { receipt, peer_info }
|
SignalInfo::HolePunch {
|
||||||
|
receipt_nonce,
|
||||||
|
peer_info,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
veilid_capnp::operation_signal::ReverseConnect(r) => {
|
veilid_capnp::operation_signal::ReverseConnect(r) => {
|
||||||
// Extract reverse connect reader
|
// Extract reverse connect reader
|
||||||
@ -68,18 +63,17 @@ pub fn decode_signal_info(
|
|||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(_) => return Err(rpc_error_internal("invalid reverse connect")),
|
Err(_) => return Err(rpc_error_internal("invalid reverse connect")),
|
||||||
};
|
};
|
||||||
let receipt = r
|
let receipt_nonce =
|
||||||
.get_receipt()
|
decode_nonce(&r.get_receipt_nonce().map_err(map_error_capnp_error!())?);
|
||||||
.map_err(map_error_protocol!(
|
|
||||||
"invalid receipt in reverse connect signal info"
|
|
||||||
))?
|
|
||||||
.to_vec();
|
|
||||||
let pi_reader = r.get_peer_info().map_err(map_error_protocol!(
|
let pi_reader = r.get_peer_info().map_err(map_error_protocol!(
|
||||||
"invalid peer info in reverse connect signal info"
|
"invalid peer info in reverse connect signal info"
|
||||||
))?;
|
))?;
|
||||||
let peer_info = decode_peer_info(&pi_reader, true)?;
|
let peer_info = decode_peer_info(&pi_reader, true)?;
|
||||||
|
|
||||||
SignalInfo::ReverseConnect { receipt, peer_info }
|
SignalInfo::ReverseConnect {
|
||||||
|
receipt_nonce,
|
||||||
|
peer_info,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -872,7 +872,7 @@ impl RPCProcessor {
|
|||||||
rpcreader: RPCMessageReader,
|
rpcreader: RPCMessageReader,
|
||||||
) -> Result<(), RPCError> {
|
) -> Result<(), RPCError> {
|
||||||
//
|
//
|
||||||
let (alternate_port, redirect, dial_info, rcpt_data) = {
|
let (redirect, dial_info, receipt_nonce, min_version, max_version) = {
|
||||||
let operation = rpcreader
|
let operation = rpcreader
|
||||||
.reader
|
.reader
|
||||||
.get_root::<veilid_capnp::operation::Reader>()
|
.get_root::<veilid_capnp::operation::Reader>()
|
||||||
@ -893,18 +893,20 @@ impl RPCProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Parse out fields
|
// Parse out fields
|
||||||
let alternate_port = vdi_reader.get_alternate_port();
|
let min_version = vdi_reader.get_min_version();
|
||||||
|
let max_version = vdi_reader.get_max_version();
|
||||||
let redirect = vdi_reader.get_redirect();
|
let redirect = vdi_reader.get_redirect();
|
||||||
let dial_info = decode_dial_info(
|
let dial_info = decode_dial_info(
|
||||||
&vdi_reader
|
&vdi_reader
|
||||||
.get_dial_info()
|
.get_dial_info()
|
||||||
.map_err(map_error_internal!("no valid dial info"))?,
|
.map_err(map_error_internal!("no valid dial info"))?,
|
||||||
)?;
|
)?;
|
||||||
let rcpt_data = vdi_reader
|
let rn_reader = vdi_reader
|
||||||
.get_receipt()
|
.get_receipt_nonce()
|
||||||
.map_err(map_error_internal!("no valid receipt"))?;
|
.map_err(map_error_internal!("no valid receipt nonce"))?;
|
||||||
|
let receipt_nonce = decode_nonce(&rn_reader);
|
||||||
|
|
||||||
(alternate_port, redirect, dial_info, rcpt_data)
|
(redirect, dial_info, receipt_nonce, min_version, max_version)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Redirect this request if we are asked to
|
// Redirect this request if we are asked to
|
||||||
@ -946,18 +948,13 @@ impl RPCProcessor {
|
|||||||
respond_to.set_none(());
|
respond_to.set_none(());
|
||||||
let detail = question.reborrow().init_detail();
|
let detail = question.reborrow().init_detail();
|
||||||
let mut vdi_builder = detail.init_validate_dial_info();
|
let mut vdi_builder = detail.init_validate_dial_info();
|
||||||
vdi_builder.set_alternate_port(alternate_port);
|
|
||||||
vdi_builder.set_redirect(false);
|
vdi_builder.set_redirect(false);
|
||||||
|
vdi_builder.set_min_version(min_version);
|
||||||
|
vdi_builder.set_max_version(max_version);
|
||||||
let mut di_builder = vdi_builder.reborrow().init_dial_info();
|
let mut di_builder = vdi_builder.reborrow().init_dial_info();
|
||||||
encode_dial_info(&dial_info, &mut di_builder)?;
|
encode_dial_info(&dial_info, &mut di_builder)?;
|
||||||
let r_builder = vdi_builder.reborrow().init_receipt(
|
let mut rn_builder = vdi_builder.reborrow().init_receipt_nonce();
|
||||||
rcpt_data
|
encode_nonce(&receipt_nonce, &mut rn_builder);
|
||||||
.len()
|
|
||||||
.try_into()
|
|
||||||
.map_err(map_error_internal!("receipt too large"))?,
|
|
||||||
);
|
|
||||||
r_builder.copy_from_slice(rcpt_data);
|
|
||||||
|
|
||||||
vdi_msg.into_reader()
|
vdi_msg.into_reader()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -970,13 +967,25 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// Otherwise send a return receipt directly
|
// Otherwise send a return receipt directly
|
||||||
// Possibly from an alternate port
|
// Possibly from an alternate port
|
||||||
|
let version = {
|
||||||
|
#[allow(clippy::absurd_extreme_comparisons)]
|
||||||
|
if min_version > MAX_VERSION || max_version < MIN_VERSION {
|
||||||
|
return Err(rpc_error_protocol(format!(
|
||||||
|
"can't send direct receipt to {} because version is unsupported: ({},{})",
|
||||||
|
dial_info, min_version, max_version
|
||||||
|
)))
|
||||||
|
.map_err(
|
||||||
|
logthru_rpc!(debug));
|
||||||
|
}
|
||||||
|
cmp::min(max_version, MAX_VERSION)
|
||||||
|
};
|
||||||
let network_manager = self.network_manager();
|
let network_manager = self.network_manager();
|
||||||
network_manager
|
network_manager
|
||||||
.send_direct_receipt(dial_info.clone(), rcpt_data, alternate_port)
|
.send_out_of_band_receipt(dial_info.clone(), version, receipt_nonce, [])
|
||||||
.await
|
.await
|
||||||
.map_err(map_error_string!())
|
.map_err(map_error_string!())
|
||||||
.map_err(
|
.map_err(
|
||||||
logthru_net!(error "failed to send direct receipt to dial info: {}, alternate_port={}", dial_info, alternate_port),
|
logthru_net!(error "failed to send direct receipt to dial info: {}, version={}", dial_info, version),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1157,7 +1166,7 @@ impl RPCProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn process_return_receipt(&self, rpcreader: RPCMessageReader) -> Result<(), RPCError> {
|
async fn process_return_receipt(&self, rpcreader: RPCMessageReader) -> Result<(), RPCError> {
|
||||||
let rcpt_data = {
|
let (receipt_nonce, extra_data) = {
|
||||||
let operation = rpcreader
|
let operation = rpcreader
|
||||||
.reader
|
.reader
|
||||||
.get_root::<veilid_capnp::operation::Reader>()
|
.get_root::<veilid_capnp::operation::Reader>()
|
||||||
@ -1177,18 +1186,24 @@ impl RPCProcessor {
|
|||||||
_ => panic!("invalid operation type in process_return_receipt"),
|
_ => panic!("invalid operation type in process_return_receipt"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get receipt data
|
// Get receipt nonce
|
||||||
let rcpt_data = rr_reader
|
let rn_reader = rr_reader
|
||||||
.get_receipt()
|
.get_receipt_nonce()
|
||||||
.map_err(map_error_internal!("no valid receipt"))?;
|
.map_err(map_error_internal!("no valid receipt_nonce"))?;
|
||||||
|
let receipt_nonce = decode_nonce(&rn_reader);
|
||||||
|
|
||||||
rcpt_data.to_vec()
|
// Get receipt extra data
|
||||||
|
let extra_data = rr_reader
|
||||||
|
.get_extra_data()
|
||||||
|
.map_err(map_error_internal!("no valid extra data"))?;
|
||||||
|
|
||||||
|
(receipt_nonce, extra_data.to_vec())
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle it
|
// Handle it
|
||||||
let network_manager = self.network_manager();
|
let network_manager = self.network_manager();
|
||||||
network_manager
|
network_manager
|
||||||
.handle_in_band_receipt(rcpt_data, rpcreader.header.peer_noderef)
|
.handle_in_band_receipt(receipt_nonce, extra_data, rpcreader.header.peer_noderef)
|
||||||
.await
|
.await
|
||||||
.map_err(map_error_string!())
|
.map_err(map_error_string!())
|
||||||
}
|
}
|
||||||
@ -1567,7 +1582,6 @@ impl RPCProcessor {
|
|||||||
peer: NodeRef,
|
peer: NodeRef,
|
||||||
dial_info: DialInfo,
|
dial_info: DialInfo,
|
||||||
redirect: bool,
|
redirect: bool,
|
||||||
alternate_port: bool,
|
|
||||||
) -> Result<bool, RPCError> {
|
) -> Result<bool, RPCError> {
|
||||||
let network_manager = self.network_manager();
|
let network_manager = self.network_manager();
|
||||||
let receipt_time = ms_to_us(
|
let receipt_time = ms_to_us(
|
||||||
@ -1588,21 +1602,18 @@ impl RPCProcessor {
|
|||||||
let mut vdi_builder = detail.init_validate_dial_info();
|
let mut vdi_builder = detail.init_validate_dial_info();
|
||||||
|
|
||||||
// Generate receipt and waitable eventual so we can see if we get the receipt back
|
// Generate receipt and waitable eventual so we can see if we get the receipt back
|
||||||
let (rcpt_data, eventual_value) = network_manager
|
let (receipt_nonce, eventual_value) = network_manager
|
||||||
.generate_single_shot_receipt(receipt_time, [])
|
.generate_single_shot_receipt(receipt_time)
|
||||||
.map_err(map_error_string!())?;
|
.map_err(map_error_string!())?;
|
||||||
|
|
||||||
vdi_builder.set_redirect(redirect);
|
vdi_builder.set_redirect(redirect);
|
||||||
vdi_builder.set_alternate_port(alternate_port);
|
vdi_builder.set_min_version(MIN_VERSION);
|
||||||
|
vdi_builder.set_max_version(MAX_VERSION);
|
||||||
let mut di_builder = vdi_builder.reborrow().init_dial_info();
|
let mut di_builder = vdi_builder.reborrow().init_dial_info();
|
||||||
encode_dial_info(&dial_info, &mut di_builder)?;
|
encode_dial_info(&dial_info, &mut di_builder)?;
|
||||||
let r_builder = vdi_builder.reborrow().init_receipt(
|
let mut rn_builder = vdi_builder.reborrow().init_receipt_nonce();
|
||||||
rcpt_data
|
encode_nonce(&receipt_nonce, &mut rn_builder);
|
||||||
.len()
|
|
||||||
.try_into()
|
|
||||||
.map_err(map_error_internal!("receipt too large"))?,
|
|
||||||
);
|
|
||||||
r_builder.copy_from_slice(rcpt_data.as_slice());
|
|
||||||
(vdi_msg.into_reader(), eventual_value)
|
(vdi_msg.into_reader(), eventual_value)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1614,7 +1625,15 @@ impl RPCProcessor {
|
|||||||
log_net!(debug "waiting for validate_dial_info receipt");
|
log_net!(debug "waiting for validate_dial_info receipt");
|
||||||
// Wait for receipt
|
// Wait for receipt
|
||||||
match eventual_value.await.take_value().unwrap() {
|
match eventual_value.await.take_value().unwrap() {
|
||||||
ReceiptEvent::Returned(_) => {
|
ReceiptEvent::ReturnedInBand {
|
||||||
|
inbound_noderef: _,
|
||||||
|
extra_data:_
|
||||||
|
} => {
|
||||||
|
Err(rpc_error_internal("validate_dial_info receipt should be returned out-of-band"))
|
||||||
|
}
|
||||||
|
ReceiptEvent::ReturnedOutOfBand {
|
||||||
|
extra_data:_
|
||||||
|
} => {
|
||||||
log_net!(debug "validate_dial_info receipt returned");
|
log_net!(debug "validate_dial_info receipt returned");
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@ -1764,12 +1783,10 @@ impl RPCProcessor {
|
|||||||
&self,
|
&self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
safety_route: Option<&SafetyRouteSpec>,
|
safety_route: Option<&SafetyRouteSpec>,
|
||||||
rcpt_data: B,
|
receipt_nonce: Nonce,
|
||||||
|
extra_data: B,
|
||||||
) -> Result<(), RPCError> {
|
) -> Result<(), RPCError> {
|
||||||
// Validate receipt before we send it, otherwise this may be arbitrary data!
|
let extra_data = extra_data.as_ref();
|
||||||
let _ = Receipt::from_signed_data(rcpt_data.as_ref())
|
|
||||||
.map_err(|_| "failed to validate direct receipt".to_owned())
|
|
||||||
.map_err(map_error_string!())?;
|
|
||||||
|
|
||||||
let rr_msg = {
|
let rr_msg = {
|
||||||
let mut rr_msg = ::capnp::message::Builder::new_default();
|
let mut rr_msg = ::capnp::message::Builder::new_default();
|
||||||
@ -1778,12 +1795,13 @@ impl RPCProcessor {
|
|||||||
let mut respond_to = question.reborrow().init_respond_to();
|
let mut respond_to = question.reborrow().init_respond_to();
|
||||||
respond_to.set_none(());
|
respond_to.set_none(());
|
||||||
let detail = question.reborrow().init_detail();
|
let detail = question.reborrow().init_detail();
|
||||||
let rr_builder = detail.init_return_receipt();
|
let mut rr_builder = detail.init_return_receipt();
|
||||||
|
let mut rn_builder = rr_builder.reborrow().init_receipt_nonce();
|
||||||
let r_builder = rr_builder.init_receipt(rcpt_data.as_ref().len().try_into().map_err(
|
encode_nonce(&receipt_nonce, &mut rn_builder);
|
||||||
map_error_protocol!("invalid receipt length in return receipt"),
|
let ed_builder = rr_builder.init_extra_data(extra_data.len().try_into().map_err(
|
||||||
|
map_error_protocol!("invalid extra data length in return receipt"),
|
||||||
)?);
|
)?);
|
||||||
r_builder.copy_from_slice(rcpt_data.as_ref());
|
ed_builder.copy_from_slice(extra_data);
|
||||||
|
|
||||||
rr_msg.into_reader()
|
rr_msg.into_reader()
|
||||||
};
|
};
|
||||||
|
@ -14,6 +14,7 @@ pub use attachment_manager::AttachmentManager;
|
|||||||
pub use core::str::FromStr;
|
pub use core::str::FromStr;
|
||||||
pub use dht::crypto::Crypto;
|
pub use dht::crypto::Crypto;
|
||||||
pub use dht::key::{generate_secret, sign, verify, DHTKey, DHTKeySecret, DHTSignature};
|
pub use dht::key::{generate_secret, sign, verify, DHTKey, DHTKeySecret, DHTSignature};
|
||||||
|
pub use dht::receipt::ReceiptNonce;
|
||||||
pub use intf::BlockStore;
|
pub use intf::BlockStore;
|
||||||
pub use intf::ProtectedStore;
|
pub use intf::ProtectedStore;
|
||||||
pub use intf::TableStore;
|
pub use intf::TableStore;
|
||||||
@ -337,6 +338,8 @@ pub struct NodeStatus {
|
|||||||
pub struct NodeInfo {
|
pub struct NodeInfo {
|
||||||
pub network_class: NetworkClass,
|
pub network_class: NetworkClass,
|
||||||
pub outbound_protocols: ProtocolSet,
|
pub outbound_protocols: ProtocolSet,
|
||||||
|
pub min_version: u8,
|
||||||
|
pub max_version: u8,
|
||||||
pub dial_info_detail_list: Vec<DialInfoDetail>,
|
pub dial_info_detail_list: Vec<DialInfoDetail>,
|
||||||
pub relay_peer_info: Option<Box<PeerInfo>>,
|
pub relay_peer_info: Option<Box<PeerInfo>>,
|
||||||
}
|
}
|
||||||
@ -1039,6 +1042,36 @@ impl DialInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_vec_from_short<S: AsRef<str>, H: AsRef<str>>(
|
||||||
|
short: S,
|
||||||
|
hostname: H,
|
||||||
|
) -> Result<Vec<Self>, VeilidAPIError> {
|
||||||
|
let short = short.as_ref();
|
||||||
|
let hostname = hostname.as_ref();
|
||||||
|
|
||||||
|
if short.len() < 2 {
|
||||||
|
return Err(parse_error!("invalid short url length", short));
|
||||||
|
}
|
||||||
|
let url = match &short[0..1] {
|
||||||
|
"U" => {
|
||||||
|
format!("udp://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
"T" => {
|
||||||
|
format!("tcp://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
"W" => {
|
||||||
|
format!("ws://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
"S" => {
|
||||||
|
format!("wss://{}:{}", hostname, &short[1..])
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(parse_error!("invalid short url type", short));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Self::try_vec_from_url(url)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_vec_from_url<S: AsRef<str>>(url: S) -> Result<Vec<Self>, VeilidAPIError> {
|
pub fn try_vec_from_url<S: AsRef<str>>(url: S) -> Result<Vec<Self>, VeilidAPIError> {
|
||||||
let url = url.as_ref();
|
let url = url.as_ref();
|
||||||
let split_url = SplitUrl::from_str(url)
|
let split_url = SplitUrl::from_str(url)
|
||||||
@ -1088,6 +1121,60 @@ impl DialInfo {
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn to_short(&self) -> (String, String) {
|
||||||
|
match self {
|
||||||
|
DialInfo::UDP(di) => (
|
||||||
|
format!("U{}", di.socket_address.port()),
|
||||||
|
intf::ptr_lookup(di.socket_address.to_ip_addr())
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| di.socket_address.to_string()),
|
||||||
|
),
|
||||||
|
DialInfo::TCP(di) => (
|
||||||
|
format!("T{}", di.socket_address.port()),
|
||||||
|
intf::ptr_lookup(di.socket_address.to_ip_addr())
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| di.socket_address.to_string()),
|
||||||
|
),
|
||||||
|
DialInfo::WS(di) => {
|
||||||
|
let mut split_url = SplitUrl::from_str(&format!("ws://{}", di.request)).unwrap();
|
||||||
|
if let SplitUrlHost::IpAddr(a) = split_url.host {
|
||||||
|
if let Ok(host) = intf::ptr_lookup(a).await {
|
||||||
|
split_url.host = SplitUrlHost::Hostname(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"W{}{}",
|
||||||
|
split_url.port.unwrap_or(80),
|
||||||
|
split_url
|
||||||
|
.path
|
||||||
|
.map(|p| format!("/{}", p))
|
||||||
|
.unwrap_or_default()
|
||||||
|
),
|
||||||
|
split_url.host.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DialInfo::WSS(di) => {
|
||||||
|
let mut split_url = SplitUrl::from_str(&format!("wss://{}", di.request)).unwrap();
|
||||||
|
if let SplitUrlHost::IpAddr(a) = split_url.host {
|
||||||
|
if let Ok(host) = intf::ptr_lookup(a).await {
|
||||||
|
split_url.host = SplitUrlHost::Hostname(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"S{}{}",
|
||||||
|
split_url.port.unwrap_or(443),
|
||||||
|
split_url
|
||||||
|
.path
|
||||||
|
.map(|p| format!("/{}", p))
|
||||||
|
.unwrap_or_default()
|
||||||
|
),
|
||||||
|
split_url.host.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub async fn to_url(&self) -> String {
|
pub async fn to_url(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
DialInfo::UDP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr())
|
DialInfo::UDP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr())
|
||||||
@ -1395,12 +1482,12 @@ cfg_if! {
|
|||||||
pub enum SignalInfo {
|
pub enum SignalInfo {
|
||||||
HolePunch {
|
HolePunch {
|
||||||
// UDP Hole Punch Request
|
// UDP Hole Punch Request
|
||||||
receipt: Vec<u8>, // Receipt to be returned after the hole punch
|
receipt_nonce: ReceiptNonce, // Receipt to be returned after the hole punch
|
||||||
peer_info: PeerInfo, // Sender's peer info
|
peer_info: PeerInfo, // Sender's peer info
|
||||||
},
|
},
|
||||||
ReverseConnect {
|
ReverseConnect {
|
||||||
// Reverse Connection Request
|
// Reverse Connection Request
|
||||||
receipt: Vec<u8>, // Receipt to be returned by the reverse connection
|
receipt_nonce: ReceiptNonce, // Receipt to be returned by the reverse connection
|
||||||
peer_info: PeerInfo, // Sender's peer info
|
peer_info: PeerInfo, // Sender's peer info
|
||||||
},
|
},
|
||||||
// XXX: WebRTC
|
// XXX: WebRTC
|
||||||
@ -1734,7 +1821,6 @@ impl VeilidAPI {
|
|||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
dial_info: DialInfo,
|
dial_info: DialInfo,
|
||||||
redirect: bool,
|
redirect: bool,
|
||||||
alternate_port: bool,
|
|
||||||
) -> Result<bool, VeilidAPIError> {
|
) -> Result<bool, VeilidAPIError> {
|
||||||
let rpc = self.rpc_processor()?;
|
let rpc = self.rpc_processor()?;
|
||||||
let routing_table = rpc.routing_table();
|
let routing_table = rpc.routing_table();
|
||||||
@ -1742,7 +1828,7 @@ impl VeilidAPI {
|
|||||||
None => return Err(VeilidAPIError::NodeNotFound { node_id }),
|
None => return Err(VeilidAPIError::NodeNotFound { node_id }),
|
||||||
Some(nr) => nr,
|
Some(nr) => nr,
|
||||||
};
|
};
|
||||||
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
|
.await
|
||||||
.map_err(map_rpc_error!())
|
.map_err(map_rpc_error!())
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ cfg_if::cfg_if! {
|
|||||||
if #[cfg(unix)] {
|
if #[cfg(unix)] {
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::os::unix::prelude::PermissionsExt;
|
use std::os::unix::prelude::PermissionsExt;
|
||||||
use nix::unistd::{chown, Uid, Gid};
|
use nix::unistd::{Uid, Gid};
|
||||||
|
|
||||||
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
|
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
|
||||||
{
|
{
|
||||||
@ -200,7 +200,7 @@ cfg_if::cfg_if! {
|
|||||||
std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path '{:?}': {}", path, e))?;
|
std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path '{:?}': {}", path, e))?;
|
||||||
}
|
}
|
||||||
if meta.uid() != uid.as_raw() || meta.gid() != gid.as_raw() {
|
if meta.uid() != uid.as_raw() || meta.gid() != gid.as_raw() {
|
||||||
chown(path, Some(uid), Some(gid)).map_err(|e| format!("unable to set correct owner on path '{:?}': {}", path, e))?;
|
return Err(format!("path has incorrect owner/group: {:?}", path));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,18 @@ pub async fn run_veilid_server(
|
|||||||
settings: Settings,
|
settings: Settings,
|
||||||
logs: VeilidLogs,
|
logs: VeilidLogs,
|
||||||
server_mode: ServerMode,
|
server_mode: ServerMode,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
run_veilid_server_internal(settings, logs, server_mode)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("{}", e);
|
||||||
|
e
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub async fn run_veilid_server_internal(
|
||||||
|
settings: Settings,
|
||||||
|
logs: VeilidLogs,
|
||||||
|
server_mode: ServerMode,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let settingsr = settings.read();
|
let settingsr = settings.read();
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ core:
|
|||||||
set_value_timeout:
|
set_value_timeout:
|
||||||
set_value_count: 20
|
set_value_count: 20
|
||||||
set_value_fanout: 5
|
set_value_fanout: 5
|
||||||
min_peer_count: 20
|
min_peer_count: 1 # 20
|
||||||
min_peer_refresh_time_ms: 2000
|
min_peer_refresh_time_ms: 2000
|
||||||
validate_dial_info_receipt_time_ms: 5000
|
validate_dial_info_receipt_time_ms: 5000
|
||||||
upnp: false
|
upnp: false
|
||||||
@ -97,8 +97,8 @@ core:
|
|||||||
enable_local_peer_scope: false
|
enable_local_peer_scope: false
|
||||||
restricted_nat_retries: 3
|
restricted_nat_retries: 3
|
||||||
tls:
|
tls:
|
||||||
certificate_path: '/etc/veilid-server/server.crt'
|
certificate_path: '%CERTIFICATE_DIRECTORY%/server.crt'
|
||||||
private_key_path: '/etc/veilid-server/private/server.key'
|
private_key_path: '%PRIVATE_KEY_DIRECTORY%/server.key'
|
||||||
connection_initial_timeout_ms: 2000
|
connection_initial_timeout_ms: 2000
|
||||||
application:
|
application:
|
||||||
https:
|
https:
|
||||||
@ -150,6 +150,14 @@ core:
|
|||||||
.replace(
|
.replace(
|
||||||
"%INSECURE_FALLBACK_DIRECTORY%",
|
"%INSECURE_FALLBACK_DIRECTORY%",
|
||||||
&Settings::get_default_protected_store_insecure_fallback_directory().to_string_lossy(),
|
&Settings::get_default_protected_store_insecure_fallback_directory().to_string_lossy(),
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
"%CERTIFICATE_DIRECTORY%",
|
||||||
|
&Settings::get_default_certificate_directory().to_string_lossy(),
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
"%PRIVATE_KEY_DIRECTORY%",
|
||||||
|
&Settings::get_default_private_key_directory().to_string_lossy(),
|
||||||
);
|
);
|
||||||
config::Config::builder()
|
config::Config::builder()
|
||||||
.add_source(config::File::from_str(
|
.add_source(config::File::from_str(
|
||||||
@ -723,68 +731,123 @@ impl Settings {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_root() -> bool {
|
pub fn get_default_config_path() -> PathBuf {
|
||||||
cfg_if::cfg_if! {
|
#[cfg(unix)]
|
||||||
if #[cfg(unix)] {
|
{
|
||||||
use nix::unistd::Uid;
|
let globalpath = PathBuf::from("/etc/veilid-server/veilid-server.conf");
|
||||||
Uid::effective().is_root()
|
if globalpath.exists() {
|
||||||
} else {
|
return globalpath;
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_config_path() -> PathBuf {
|
let mut cfg_path = if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid")
|
||||||
let mut default_config_path = if Self::is_root() {
|
{
|
||||||
PathBuf::from("/etc/veilid-server")
|
|
||||||
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
|
||||||
PathBuf::from(my_proj_dirs.config_dir())
|
PathBuf::from(my_proj_dirs.config_dir())
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from("./")
|
PathBuf::from("./")
|
||||||
};
|
};
|
||||||
|
cfg_path.push("veilid-server.conf");
|
||||||
|
|
||||||
default_config_path.push("veilid-server.conf");
|
cfg_path
|
||||||
|
|
||||||
default_config_path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_table_store_path() -> PathBuf {
|
pub fn get_default_table_store_path() -> PathBuf {
|
||||||
let mut default_db_path = if Self::is_root() {
|
#[cfg(unix)]
|
||||||
PathBuf::from("/var/db/veilid-server")
|
{
|
||||||
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
let globalpath = PathBuf::from("/var/db/veilid-server/table_store");
|
||||||
|
if globalpath.exists() {
|
||||||
|
return globalpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ts_path = if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
||||||
PathBuf::from(my_proj_dirs.data_local_dir())
|
PathBuf::from(my_proj_dirs.data_local_dir())
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from("./")
|
PathBuf::from("./")
|
||||||
};
|
};
|
||||||
default_db_path.push("table_store");
|
ts_path.push("table_store");
|
||||||
|
|
||||||
default_db_path
|
ts_path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_block_store_path() -> PathBuf {
|
pub fn get_default_block_store_path() -> PathBuf {
|
||||||
let mut default_db_path = if Self::is_root() {
|
#[cfg(unix)]
|
||||||
PathBuf::from("/var/db/veilid-server")
|
{
|
||||||
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
let globalpath = PathBuf::from("/var/db/veilid-server/block_store");
|
||||||
|
if globalpath.exists() {
|
||||||
|
return globalpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut bs_path = if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
||||||
PathBuf::from(my_proj_dirs.data_local_dir())
|
PathBuf::from(my_proj_dirs.data_local_dir())
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from("./")
|
PathBuf::from("./")
|
||||||
};
|
};
|
||||||
default_db_path.push("block_store");
|
bs_path.push("block_store");
|
||||||
|
|
||||||
default_db_path
|
bs_path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_protected_store_insecure_fallback_directory() -> PathBuf {
|
pub fn get_default_protected_store_insecure_fallback_directory() -> PathBuf {
|
||||||
let mut default_db_path = if Self::is_root() {
|
#[cfg(unix)]
|
||||||
PathBuf::from("/var/db/veilid-server")
|
{
|
||||||
} else if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
let globalpath = PathBuf::from("/var/db/veilid-server/protected_store");
|
||||||
|
if globalpath.exists() {
|
||||||
|
return globalpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ps_path = if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
||||||
PathBuf::from(my_proj_dirs.data_local_dir())
|
PathBuf::from(my_proj_dirs.data_local_dir())
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from("./")
|
PathBuf::from("./")
|
||||||
};
|
};
|
||||||
default_db_path.push("protected_store");
|
ps_path.push("protected_store");
|
||||||
|
|
||||||
default_db_path
|
ps_path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_default_certificate_directory() -> PathBuf {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let mut globalpath = PathBuf::from("/etc/veilid-server");
|
||||||
|
if globalpath.exists() {
|
||||||
|
globalpath.push("ssl");
|
||||||
|
globalpath.push("certs");
|
||||||
|
return globalpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut c_path = if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
||||||
|
PathBuf::from(my_proj_dirs.data_local_dir())
|
||||||
|
} else {
|
||||||
|
PathBuf::from("./")
|
||||||
|
};
|
||||||
|
c_path.push("ssl");
|
||||||
|
c_path.push("certs");
|
||||||
|
c_path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_default_private_key_directory() -> PathBuf {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let mut globalpath = PathBuf::from("/etc/veilid-server");
|
||||||
|
if globalpath.exists() {
|
||||||
|
globalpath.push("ssl");
|
||||||
|
globalpath.push("keys");
|
||||||
|
return globalpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pk_path = if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
||||||
|
PathBuf::from(my_proj_dirs.data_local_dir())
|
||||||
|
} else {
|
||||||
|
PathBuf::from("./")
|
||||||
|
};
|
||||||
|
pk_path.push("ssl");
|
||||||
|
pk_path.push("keys");
|
||||||
|
pk_path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_core_config_callback(&self) -> veilid_core::ConfigCallback {
|
pub fn get_core_config_callback(&self) -> veilid_core::ConfigCallback {
|
||||||
|
@ -6,6 +6,7 @@ use async_std::task;
|
|||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use signal_hook::consts::signal::*;
|
use signal_hook::consts::signal::*;
|
||||||
use signal_hook_async_std::Signals;
|
use signal_hook_async_std::Signals;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
async fn handle_signals(mut signals: Signals) {
|
async fn handle_signals(mut signals: Signals) {
|
||||||
while let Some(signal) = signals.next().await {
|
while let Some(signal) = signals.next().await {
|
||||||
@ -26,8 +27,25 @@ pub fn run_daemon(settings: Settings, _matches: ArgMatches) -> Result<(), String
|
|||||||
let daemon = {
|
let daemon = {
|
||||||
let mut daemon = daemonize::Daemonize::new();
|
let mut daemon = daemonize::Daemonize::new();
|
||||||
let s = settings.read();
|
let s = settings.read();
|
||||||
if let Some(pid_file) = &s.daemon.pid_file {
|
if let Some(pid_file) = s.daemon.pid_file.clone() {
|
||||||
daemon = daemon.pid_file(pid_file); //.chown_pid_file(true);
|
daemon = daemon.pid_file(pid_file.clone()); //.chown_pid_file(true);
|
||||||
|
daemon = daemon.exit_action(move || {
|
||||||
|
// wait for pid file to exist before exiting parent
|
||||||
|
let pid_path = std::path::Path::new(&pid_file);
|
||||||
|
loop {
|
||||||
|
if let Ok(mut f) = std::fs::File::open(pid_path) {
|
||||||
|
let mut s = String::new();
|
||||||
|
if f.read_to_string(&mut s).is_ok()
|
||||||
|
&& !s.is_empty()
|
||||||
|
&& s.parse::<u32>().is_ok()
|
||||||
|
{
|
||||||
|
println!("pidfile found");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if let Some(chroot) = &s.daemon.chroot {
|
if let Some(chroot) = &s.daemon.chroot {
|
||||||
daemon = daemon.chroot(chroot);
|
daemon = daemon.chroot(chroot);
|
||||||
|
Loading…
Reference in New Issue
Block a user