1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use super::*;
/// One APDU handed to a local-issuance send.
#[derive(Debug, Clone, Copy)]
pub struct IssuedApdu<'a> {
/// Encoded APDU to wrap in an NPDU.
pub apdu: &'a [u8],
/// Local data-link destination.
pub next_hop: &'a [u8],
/// Routed NPDU destination (DNET/DADR), when the peer is behind a router.
pub destination: Option<&'a NpduAddress>,
/// Whether the NPDU sets the data-expecting-reply flag.
pub expecting_reply: bool,
/// Network priority of the NPDU.
pub priority: NetworkPriority,
}
impl<T: TransportPort> NetworkLayer<T> {
/// Seal original-direct response issuance before joining owned server work.
/// Queued writes observe this irreversible seal; writes already started
/// remain bounded but cannot be retracted. Network stop/drop also seals.
pub fn seal_responses(&self) {
self.response_scope.seal();
}
}
impl<T: TransportPort + 'static> NetworkLayer<T> {
/// Issue one local unicast operation after encoding its complete NPDU.
///
/// `destination` selects routed NPDU addressing; `next_hop` is always the
/// local data-link destination. The synchronous callback runs exactly once
/// after encoding succeeds, immediately before calling/polling the transport
/// send. Constructing this future does not call it. Encoding failure never
/// calls it; later transport failure does not undo issuance. This boundary
/// does not establish physical emission or remote receipt. A `destination`
/// on network 0 or 0xFFFF is an encoding failure: this unicast reaches
/// one next hop, and a global broadcast goes out through
/// [`NetworkLayer::broadcast_global_apdu`] instead (#1380). A
/// `destination` with no DADR is a remote broadcast, which carries only
/// an Unconfirmed-Request APDU (Clause 6.3), so any other APDU to it is
/// an encoding failure too (#1479). With no `destination`, a `next_hop`
/// that is the link's broadcast or another group address is a local
/// broadcast as well, but this layer doesn't ask the transport on every
/// unicast, so that one isn't checked, as with
/// [`NetworkLayer::send_apdu`]: a response goes back to the MAC its
/// request came from.
///
/// Used by server transaction owners whose lifetime ends at local issuance,
/// independently of the transport future's eventual Result.
pub async fn send_apdu_on_issuance(
&self,
apdu: &[u8],
next_hop: &[u8],
destination: Option<&NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
on_issuance: impl FnOnce() + Send,
) -> Result<(), Error> {
self.send_response_apdu_on_issuance(
IssuedApdu {
apdu,
next_hop,
destination,
expecting_reply,
priority,
},
&crate::response_route::ResponseRoute::unverified(),
on_issuance,
)
.await
}
/// Encode and issue a response using its immutable ingress route.
///
/// Direct responses use only the matching original direct socket. A
/// missing, invalid or stale capability cannot fall back to ordinary unicast.
/// The callback has the same local issuance contract as
/// [`Self::send_apdu_on_issuance`]; neither issuance nor success proves receipt.
pub async fn send_response_apdu_on_issuance(
&self,
issued: IssuedApdu<'_>,
route: &crate::response_route::ResponseRoute,
on_issuance: impl FnOnce() + Send,
) -> Result<(), Error> {
let IssuedApdu {
apdu,
next_hop,
destination,
expecting_reply,
priority,
} = issued;
crate::response_route::check_response_destination(destination)?;
if destination.is_some_and(|destination| destination.mac_address.is_empty()) {
check_broadcast_apdu(apdu, "a destination with no DADR (a remote broadcast)")?;
}
let buf = crate::response_route::encode_checked_response_npdu(
apdu,
destination,
expecting_reply,
priority,
)?;
let direct = route.direct()?;
on_issuance();
if let Some(direct) = direct {
return direct.send(&buf, &self.response_scope).await;
}
self.transport
.send_unicast_with_data_attributes(&buf, next_hop, &[])
.await
}
}