From 66dc6dc58840ba08fa55800ddf9d6ebdc6013248 Mon Sep 17 00:00:00 2001 From: matheus23 Date: Thu, 19 Feb 2026 11:35:18 +0000 Subject: [PATCH] deploy: 74e78c2114d97e87ab5b4458824d4909f41b98ae --- pr/436/docs/help.html | 2 +- .../docs/iroh_quinn/enum.ConnectionError.html | 6 +- .../iroh_quinn/trait.NetworkChangeHint.html | 4 +- .../iroh_quinn_proto/enum.ClosePathError.html | 6 +- .../enum.ConnectionError.html | 6 +- pr/436/docs/iroh_quinn_proto/enum.Event.html | 4 +- .../docs/iroh_quinn_proto/enum.PathError.html | 6 +- .../docs/iroh_quinn_proto/enum.PathEvent.html | 6 +- .../iroh_quinn_proto/enum.PathStatus.html | 6 +- .../enum.SetPathStatusError.html | 6 +- .../iroh_quinn_proto/struct.ClosedPath.html | 6 +- .../iroh_quinn_proto/struct.Connection.html | 104 +- .../struct.MultipathNotNegotiated.html | 4 +- .../iroh_quinn_proto/struct.RttEstimator.html | 10 +- .../struct.TransportError.html | 2 +- .../trait.NetworkChangeHint.html | 4 +- .../{3daf0570cb59.js => afdfd6a4eeed.js} | 2 +- .../{96c428e3eee0.js => 3691ebe673bb.js} | 2 +- .../path/{146af92373fe.js => 2b02a8829606.js} | 2 +- pr/436/docs/search.index/root.js | 2 +- .../type/{c06414ed99fe.js => e239bdddcc48.js} | 2 +- pr/436/docs/settings.html | 2 +- .../iroh_quinn_proto/connection/mod.rs.html | 14219 ++++++++-------- .../iroh_quinn_proto/connection/paths.rs.html | 1465 +- 24 files changed, 7914 insertions(+), 7964 deletions(-) rename pr/436/docs/search.index/entry/{3daf0570cb59.js => afdfd6a4eeed.js} (94%) rename pr/436/docs/search.index/function/{96c428e3eee0.js => 3691ebe673bb.js} (97%) rename pr/436/docs/search.index/path/{146af92373fe.js => 2b02a8829606.js} (98%) rename pr/436/docs/search.index/type/{c06414ed99fe.js => e239bdddcc48.js} (99%) diff --git a/pr/436/docs/help.html b/pr/436/docs/help.html index 2565ad385..09d0ca8e7 100644 --- a/pr/436/docs/help.html +++ b/pr/436/docs/help.html @@ -1 +1 @@ -Help

All

Rustdoc help

Back
\ No newline at end of file +Help

All

Rustdoc help

Back
\ No newline at end of file diff --git a/pr/436/docs/iroh_quinn/enum.ConnectionError.html b/pr/436/docs/iroh_quinn/enum.ConnectionError.html index 979acf8cb..4cce6bce3 100644 --- a/pr/436/docs/iroh_quinn/enum.ConnectionError.html +++ b/pr/436/docs/iroh_quinn/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in iroh_quinn - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in iroh_quinn - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
     VersionMismatch,
     TransportError(Error),
     ConnectionClosed(ConnectionClose),
@@ -20,8 +20,8 @@ and §

LocallyClosed

The local application closed the connection

§

CidsExhausted

The connection could not be created because not enough of the CID space is available

Try using longer connection IDs.

-

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Close> for ConnectionError

Source§

fn from(x: Close) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<CloseReason> for ConnectionError

Source§

fn from(value: CloseReason) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ReadError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: Error) -> ConnectionError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Close> for ConnectionError

Source§

fn from(x: Close) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<CloseReason> for ConnectionError

Source§

fn from(value: CloseReason) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ReadError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: Error) -> ConnectionError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn/trait.NetworkChangeHint.html b/pr/436/docs/iroh_quinn/trait.NetworkChangeHint.html index 62371139b..46a33d6fa 100644 --- a/pr/436/docs/iroh_quinn/trait.NetworkChangeHint.html +++ b/pr/436/docs/iroh_quinn/trait.NetworkChangeHint.html @@ -1,4 +1,4 @@ -NetworkChangeHint in iroh_quinn - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
+NetworkChangeHint in iroh_quinn - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
     // Required method
     fn is_path_recoverable(
         &self,
@@ -6,7 +6,7 @@
         network_path: FourTuple,
     ) -> bool;
 }
Expand description

Hints when the caller identifies a network change.

-

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

+

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

After network changes, paths may not be recoverable. In this case, waiting for the path to become idle may take longer than what is desirable. If Self::is_path_recoverable returns false, a multipath-enabled, client-side connection will establish a new path to diff --git a/pr/436/docs/iroh_quinn_proto/enum.ClosePathError.html b/pr/436/docs/iroh_quinn_proto/enum.ClosePathError.html index 8986ec4b1..8bc0ce350 100644 --- a/pr/436/docs/iroh_quinn_proto/enum.ClosePathError.html +++ b/pr/436/docs/iroh_quinn_proto/enum.ClosePathError.html @@ -1,4 +1,4 @@ -ClosePathError in iroh_quinn_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+ClosePathError in iroh_quinn_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
     MultipathNotNegotiated,
     ClosedPath,
     LastOpenPath,
@@ -6,8 +6,8 @@
 

Variants§

§

MultipathNotNegotiated

Multipath is not negotiated

§

ClosedPath

The path is already closed or was never opened

§

LastOpenPath

This is the last path, which can not be abandoned

-

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/enum.ConnectionError.html b/pr/436/docs/iroh_quinn_proto/enum.ConnectionError.html index fc34d18ff..b6046a54e 100644 --- a/pr/436/docs/iroh_quinn_proto/enum.ConnectionError.html +++ b/pr/436/docs/iroh_quinn_proto/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in iroh_quinn_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in iroh_quinn_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
     VersionMismatch,
     TransportError(TransportError),
     ConnectionClosed(ConnectionClose),
@@ -20,8 +20,8 @@ and §

LocallyClosed

The local application closed the connection

§

CidsExhausted

The connection could not be created because not enough of the CID space is available

Try using longer connection IDs.

-

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for Error

Source§

fn from(x: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for Error

Source§

fn from(x: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/enum.Event.html b/pr/436/docs/iroh_quinn_proto/enum.Event.html index 82160a6b6..790a13b1e 100644 --- a/pr/436/docs/iroh_quinn_proto/enum.Event.html +++ b/pr/436/docs/iroh_quinn_proto/enum.Event.html @@ -1,4 +1,4 @@ -Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
+Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
     HandshakeDataReady,
     Connected,
     HandshakeConfirmed,
@@ -22,7 +22,7 @@
 
§

DatagramsUnblocked

One or more application datagrams have been sent after blocking

§

Path(PathEvent)

(Multi)Path events

§

NatTraversal(Event)

Iroh’s nat traversal events

-

Trait Implementations§

Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl !Freeze for Event

§

impl !RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl !UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl !Freeze for Event

§

impl !RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl !UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

diff --git a/pr/436/docs/iroh_quinn_proto/enum.PathError.html b/pr/436/docs/iroh_quinn_proto/enum.PathError.html index 2b336df55..0d955530e 100644 --- a/pr/436/docs/iroh_quinn_proto/enum.PathError.html +++ b/pr/436/docs/iroh_quinn_proto/enum.PathError.html @@ -1,4 +1,4 @@ -PathError in iroh_quinn_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
+PathError in iroh_quinn_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
     MultipathNotNegotiated,
     ServerSideNotAllowed,
     MaxPathIdReached,
@@ -12,8 +12,8 @@
 
§

RemoteCidsExhausted

No remote CIDs available to open a new path

§

ValidationFailed

Path could not be validated and will be abandoned

§

InvalidRemoteAddress(SocketAddr)

The remote address for the path is not supported by the endpoint

-

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/enum.PathEvent.html b/pr/436/docs/iroh_quinn_proto/enum.PathEvent.html index 93f4804eb..589c37bb2 100644 --- a/pr/436/docs/iroh_quinn_proto/enum.PathEvent.html +++ b/pr/436/docs/iroh_quinn_proto/enum.PathEvent.html @@ -1,4 +1,4 @@ -PathEvent in iroh_quinn_proto - Rust

PathEvent

Enum PathEvent 

Source
pub enum PathEvent {
+PathEvent in iroh_quinn_proto - Rust

PathEvent

Enum PathEvent 

Source
pub enum PathEvent {
     Opened {
         id: PathId,
     },
@@ -37,8 +37,8 @@ changes the status.

Fields

§id: PathId

Path over which the observed address was reported, PathId::ZERO when multipath is not negotiated

§addr: SocketAddr

The address observed by the remote over this path

-

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/enum.PathStatus.html b/pr/436/docs/iroh_quinn_proto/enum.PathStatus.html index 84da84e37..ab531a6c5 100644 --- a/pr/436/docs/iroh_quinn_proto/enum.PathStatus.html +++ b/pr/436/docs/iroh_quinn_proto/enum.PathStatus.html @@ -1,4 +1,4 @@ -PathStatus in iroh_quinn_proto - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
+PathStatus in iroh_quinn_proto - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
     Available,
     Backup,
 }
Expand description

The QUIC-MULTIPATH path status

@@ -10,8 +10,8 @@ capacity.

§

Backup

Paths marked as backup will only be used if there are no available paths

If the max_idle_timeout is specified the path will be kept alive so that it does not expire.

-

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/enum.SetPathStatusError.html b/pr/436/docs/iroh_quinn_proto/enum.SetPathStatusError.html index 353749801..6fc0576ab 100644 --- a/pr/436/docs/iroh_quinn_proto/enum.SetPathStatusError.html +++ b/pr/436/docs/iroh_quinn_proto/enum.SetPathStatusError.html @@ -1,11 +1,11 @@ -SetPathStatusError in iroh_quinn_proto - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
+SetPathStatusError in iroh_quinn_proto - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
     ClosedPath,
     MultipathNotNegotiated,
 }
Expand description

Error from setting path status

Variants§

§

ClosedPath

Error indicating that a path has not been opened or has already been abandoned

§

MultipathNotNegotiated

Error indicating that this operation requires multipath to be negotiated whereas it hasn’t been

-

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/struct.ClosedPath.html b/pr/436/docs/iroh_quinn_proto/struct.ClosedPath.html index 36c314105..ac5352644 100644 --- a/pr/436/docs/iroh_quinn_proto/struct.ClosedPath.html +++ b/pr/436/docs/iroh_quinn_proto/struct.ClosedPath.html @@ -1,6 +1,6 @@ -ClosedPath in iroh_quinn_proto - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

-

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +ClosedPath in iroh_quinn_proto - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

+

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/struct.Connection.html b/pr/436/docs/iroh_quinn_proto/struct.Connection.html index 298aa2274..48a00c814 100644 --- a/pr/436/docs/iroh_quinn_proto/struct.Connection.html +++ b/pr/436/docs/iroh_quinn_proto/struct.Connection.html @@ -1,4 +1,4 @@ -Connection in iroh_quinn_proto - Rust

Connection

Struct Connection 

Source
pub struct Connection { /* private fields */ }
Expand description

Protocol state and logic for a single QUIC connection

+Connection in iroh_quinn_proto - Rust

Connection

Struct Connection 

Source
pub struct Connection { /* private fields */ }
Expand description

Protocol state and logic for a single QUIC connection

Objects of this type receive ConnectionEvents and emit EndpointEvents and application Events to make progress. To handle timeouts, a Connection returns timer updates and expects timeouts through various methods. A number of simple getter methods are exposed @@ -33,7 +33,7 @@ increasing time. Specifically, calling Instant may be interleaved in any order with a call to handle_event at that same instant; however events or timeouts with different instants must not be interleaved.

-

Implementations§

Source§

impl Connection

Source

pub fn poll_timeout(&mut self) -> Option<Instant>

Returns the next time at which handle_timeout should be called

+

Implementations§

Source§

impl Connection

Source

pub fn poll_timeout(&mut self) -> Option<Instant>

Returns the next time at which handle_timeout should be called

The value returned may change after:

  • the application performed some I/O on the connection
  • @@ -41,17 +41,17 @@ events or timeouts with different instants must not be interleaved.

  • a call to poll_transmit returned Some
  • a call was made to handle_timeout
-
Source

pub fn poll(&mut self) -> Option<Event>

Returns application-facing events

+
Source

pub fn poll(&mut self) -> Option<Event>

Returns application-facing events

Connections should be polled for events after:

  • a call was made to handle_event
  • a call was made to handle_timeout
-
Source

pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent>

Return endpoint-facing events

-
Source

pub fn streams(&mut self) -> Streams<'_>

Provide control over streams

-
Source

pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_>

Provide control over streams

-
Source

pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_>

Provide control over streams

-
Source

pub fn open_path_ensure( +

Source

pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent>

Return endpoint-facing events

+
Source

pub fn streams(&mut self) -> Streams<'_>

Provide control over streams

+
Source

pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_>

Provide control over streams

+
Source

pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_>

Provide control over streams

+
Source

pub fn open_path_ensure( &mut self, network_path: FourTuple, initial_status: PathStatus, @@ -65,7 +65,7 @@ remote addresses.

remote, should it not be known yet. We assume that if we already have a path to the remote, the OS is likely to use the same interface to talk to said remote.

See also open_path. Returns (path_id, true) if the path already existed. (path_id, false) if was opened.

-

Source

pub fn open_path( +

Source

pub fn open_path( &mut self, network_path: FourTuple, initial_status: PathStatus, @@ -73,7 +73,7 @@ the OS is likely to use the same interface to talk to said remote.

) -> Result<PathId, PathError>

Opens a new path

Further errors might occur and they will be emitted in PathEvent::LocallyClosed events. When the path is opened it will be reported as an PathEvent::Opened.

-
Source

pub fn close_path( +

Source

pub fn close_path( &mut self, now: Instant, path_id: PathId, @@ -82,32 +82,32 @@ When the path is opened it will be reported as an
Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

+

Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

There is no guarantee any of these paths are open or usable.

-
Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

-
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

-
Source

pub fn set_path_status( +

Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

+
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

+
Source

pub fn set_path_status( &mut self, path_id: PathId, status: PathStatus, ) -> Result<PathStatus, SetPathStatusError>

Sets the PathStatus for a known PathId

Returns the previous path status on success.

-
Source

pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus>

Returns the remote path status

-
Source

pub fn set_path_max_idle_timeout( +

Source

pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus>

Returns the remote path status

+
Source

pub fn set_path_max_idle_timeout( &mut self, path_id: PathId, timeout: Option<Duration>, ) -> Result<Option<Duration>, ClosedPath>

Sets the max_idle_timeout for a specific path

See TransportConfig::default_path_max_idle_timeout for details.

Returns the previous value of the setting.

-
Source

pub fn set_path_keep_alive_interval( +

Source

pub fn set_path_keep_alive_interval( &mut self, path_id: PathId, interval: Option<Duration>, ) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

See TransportConfig::default_path_keep_alive_interval for details.

Returns the previous value of the setting.

-
Source

pub fn poll_transmit( +

Source

pub fn poll_transmit( &mut self, now: Instant, max_datagrams: NonZeroUsize, @@ -121,60 +121,60 @@ for a path that was never opened locally.

max_datagrams specifies how many datagrams can be returned inside a single Transmit using GSO. This must be at least 1.

-

Source

pub fn handle_event(&mut self, event: ConnectionEvent)

Process ConnectionEvents generated by the associated Endpoint

+
Source

pub fn handle_event(&mut self, event: ConnectionEvent)

Process ConnectionEvents generated by the associated Endpoint

Will execute protocol logic upon receipt of a connection event, in turn preparing signals (including application Events, EndpointEvents and outgoing datagrams) that should be extracted through the relevant methods.

-
Source

pub fn handle_timeout(&mut self, now: Instant)

Process timer expirations

+
Source

pub fn handle_timeout(&mut self, now: Instant)

Process timer expirations

Executes protocol logic, potentially preparing signals (including application Events, EndpointEvents and outgoing datagrams) that should be extracted through the relevant methods.

It is most efficient to call this immediately after the system clock reaches the latest Instant that was output by poll_timeout; however spurious extra calls will simply no-op and therefore are safe.

-
Source

pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes)

Close a connection immediately

+
Source

pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes)

Close a connection immediately

This does not ensure delivery of outstanding data. It is the application’s responsibility to call this only when all important communications have been completed, e.g. by calling SendStream::finish on outstanding streams and waiting for the corresponding StreamEvent::Finished event.

If Streams::send_streams returns 0, all outstanding stream data has been delivered. There may still be data from the peer that has not been received.

-
Source

pub fn datagrams(&mut self) -> Datagrams<'_>

Control datagrams

-
Source

pub fn stats(&mut self) -> ConnectionStats

Returns connection statistics

-
Source

pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats>

Returns path statistics

-
Source

pub fn ping(&mut self)

Ping the remote endpoint

+
Source

pub fn datagrams(&mut self) -> Datagrams<'_>

Control datagrams

+
Source

pub fn stats(&mut self) -> ConnectionStats

Returns connection statistics

+
Source

pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats>

Returns path statistics

+
Source

pub fn ping(&mut self)

Ping the remote endpoint

Causes an ACK-eliciting packet to be transmitted on the connection.

-
Source

pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath>

Ping the remote endpoint over a specific path

+
Source

pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath>

Ping the remote endpoint over a specific path

Causes an ACK-eliciting packet to be transmitted on the path.

-
Source

pub fn force_key_update(&mut self)

Update traffic keys spontaneously

+
Source

pub fn force_key_update(&mut self)

Update traffic keys spontaneously

This can be useful for testing key updates, as they otherwise only happen infrequently.

-
Source

pub fn crypto_session(&self) -> &dyn Session

Get a session reference

-
Source

pub fn is_handshaking(&self) -> bool

Whether the connection is in the process of being established

+
Source

pub fn crypto_session(&self) -> &dyn Session

Get a session reference

+
Source

pub fn is_handshaking(&self) -> bool

Whether the connection is in the process of being established

If this returns false, the connection may be either established or closed, signaled by the emission of a Connected or ConnectionLost message respectively.

-
Source

pub fn is_closed(&self) -> bool

Whether the connection is closed

+
Source

pub fn is_closed(&self) -> bool

Whether the connection is closed

Closed connections cannot transport any further data. A connection becomes closed when either peer application intentionally closes it, or when either transport layer detects an error such as a time-out or certificate validation failure.

A ConnectionLost event is emitted with details when the connection becomes closed.

-
Source

pub fn is_drained(&self) -> bool

Whether there is no longer any need to keep the connection around

+
Source

pub fn is_drained(&self) -> bool

Whether there is no longer any need to keep the connection around

Closed connections become drained after a brief timeout to absorb any remaining in-flight packets from the peer. All drained connections have been closed.

-
Source

pub fn accepted_0rtt(&self) -> bool

For clients, if the peer accepted the 0-RTT data packets

+
Source

pub fn accepted_0rtt(&self) -> bool

For clients, if the peer accepted the 0-RTT data packets

The value is meaningless until after the handshake completes.

-
Source

pub fn has_0rtt(&self) -> bool

Whether 0-RTT is/was possible during the handshake

-
Source

pub fn has_pending_retransmits(&self) -> bool

Whether there are any pending retransmits

-
Source

pub fn side(&self) -> Side

Look up whether we’re the client or server of this Connection

-
Source

pub fn path_observed_address( +

Source

pub fn has_0rtt(&self) -> bool

Whether 0-RTT is/was possible during the handshake

+
Source

pub fn has_pending_retransmits(&self) -> bool

Whether there are any pending retransmits

+
Source

pub fn side(&self) -> Side

Look up whether we’re the client or server of this Connection

+
Source

pub fn path_observed_address( &self, path_id: PathId, ) -> Result<Option<SocketAddr>, ClosedPath>

Get the address observed by the remote over the given path

-
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

-
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller>

Current state of this connection’s congestion controller, for debugging purposes

-
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

Modify the number of remotely initiated streams that may be concurrently open

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller>

Current state of this connection’s congestion controller, for debugging purposes

+
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

Modify the number of remotely initiated streams that may be concurrently open

No streams may be opened by the peer unless fewer than count are already open. Large counts increase both minimum and worst-case memory consumption.

-
Source

pub fn set_max_concurrent_paths( +

Source

pub fn set_max_concurrent_paths( &mut self, now: Instant, count: NonZeroU32, @@ -185,16 +185,16 @@ actively reduce paths they must be closed using TransportConfig) this can not enable multipath and will fail.

-

Source

pub fn max_concurrent_streams(&self, dir: Dir) -> u64

Current number of remotely initiated streams that may be concurrently open

+
Source

pub fn max_concurrent_streams(&self, dir: Dir) -> u64

Current number of remotely initiated streams that may be concurrently open

If the target for this limit is reduced using set_max_concurrent_streams, it will not change immediately, even if fewer streams are open. Instead, it will decrement by one for each time a remotely initiated stream of matching directionality is closed.

-
Source

pub fn set_send_window(&mut self, send_window: u64)

Source

pub fn set_receive_window(&mut self, receive_window: VarInt)

Source

pub fn is_multipath_negotiated(&self) -> bool

Whether the Multipath for QUIC extension is enabled.

+
Source

pub fn set_send_window(&mut self, send_window: u64)

Source

pub fn set_receive_window(&mut self, receive_window: VarInt)

Source

pub fn is_multipath_negotiated(&self) -> bool

Whether the Multipath for QUIC extension is enabled.

Multipath is only enabled after the handshake is completed and if it was enabled by both peers.

-
Source

pub fn handle_network_change( +

Source

pub fn handle_network_change( &mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant, @@ -209,26 +209,26 @@ paths as non recoverable when necessary accelerates connectivity re-establishmen allow it altogether.

The optional hint allows callers to indicate when paths are non-recoverable and should be migrated to new a PathId.

-

Source

pub fn current_mtu(&self) -> u16

Storage size required for the largest packet that can be transmitted on all currently +

Source

pub fn current_mtu(&self) -> u16

Storage size required for the largest packet that can be transmitted on all currently available paths

Buffers passed to Connection::poll_transmit should be at least this large.

When multipath is enabled, this value is the minimum MTU across all available paths.

-
Source

pub fn add_nat_traversal_address( +

Source

pub fn add_nat_traversal_address( &mut self, address: SocketAddr, ) -> Result<(), Error>

Add addresses the local endpoint considers are reachable for nat traversal.

-
Source

pub fn remove_nat_traversal_address( +

Source

pub fn remove_nat_traversal_address( &mut self, address: SocketAddr, ) -> Result<(), Error>

Removes an address the endpoing no longer considers reachable for nat traversal

Addresses not present in the set will be silently ignored.

-
Source

pub fn get_local_nat_traversal_addresses( +

Source

pub fn get_local_nat_traversal_addresses( &self, ) -> Result<Vec<SocketAddr>, Error>

Get the current local nat traversal addresses

-
Source

pub fn get_remote_nat_traversal_addresses( +

Source

pub fn get_remote_nat_traversal_addresses( &self, ) -> Result<Vec<SocketAddr>, Error>

Get the currently advertised nat traversal addresses by the server

-
Source

pub fn initiate_nat_traversal_round( +

Source

pub fn initiate_nat_traversal_round( &mut self, now: Instant, ) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

@@ -238,7 +238,7 @@ initiated, the previous one is cancelled, and paths that have not been opened ar

Returns the server addresses that are now being probed. If addresses fail due to spurious errors, these might succeed later and not be returned in this set.

-

Trait Implementations§

Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

diff --git a/pr/436/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html b/pr/436/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html index 7f1f835d9..407554337 100644 --- a/pr/436/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html +++ b/pr/436/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html @@ -1,5 +1,5 @@ -MultipathNotNegotiated in iroh_quinn_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

Source
pub struct MultipathNotNegotiated { /* private fields */ }
Expand description

Error when the multipath extension was not negotiated, but attempted to be used.

-

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for MultipathNotNegotiated

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for MultipathNotNegotiated

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +MultipathNotNegotiated in iroh_quinn_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

Source
pub struct MultipathNotNegotiated { /* private fields */ }
Expand description

Error when the multipath extension was not negotiated, but attempted to be used.

+

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for MultipathNotNegotiated

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for MultipathNotNegotiated

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/struct.RttEstimator.html b/pr/436/docs/iroh_quinn_proto/struct.RttEstimator.html index 2ed0dd716..50aa5ba7b 100644 --- a/pr/436/docs/iroh_quinn_proto/struct.RttEstimator.html +++ b/pr/436/docs/iroh_quinn_proto/struct.RttEstimator.html @@ -1,10 +1,10 @@ -RttEstimator in iroh_quinn_proto - Rust

RttEstimator

Struct RttEstimator 

Source
pub struct RttEstimator { /* private fields */ }
Expand description

RTT estimation for a particular network path

-

Implementations§

Source§

impl RttEstimator

Source

pub fn get(&self) -> Duration

The current best RTT estimation.

-
Source

pub fn conservative(&self) -> Duration

Conservative estimate of RTT

+RttEstimator in iroh_quinn_proto - Rust

RttEstimator

Struct RttEstimator 

Source
pub struct RttEstimator { /* private fields */ }
Expand description

RTT estimation for a particular network path

+

Implementations§

Source§

impl RttEstimator

Source

pub fn get(&self) -> Duration

The current best RTT estimation.

+
Source

pub fn conservative(&self) -> Duration

Conservative estimate of RTT

Takes the maximum of smoothed and latest RTT, as recommended in 6.1.2 of the recovery spec (draft 29).

-
Source

pub fn min(&self) -> Duration

Minimum RTT registered so far for this estimator.

-

Trait Implementations§

Source§

impl Clone for RttEstimator

Source§

fn clone(&self) -> RttEstimator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RttEstimator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for RttEstimator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Source

pub fn min(&self) -> Duration

Minimum RTT registered so far for this estimator.

+

Trait Implementations§

Source§

impl Clone for RttEstimator

Source§

fn clone(&self) -> RttEstimator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RttEstimator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for RttEstimator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/436/docs/iroh_quinn_proto/struct.TransportError.html b/pr/436/docs/iroh_quinn_proto/struct.TransportError.html index 1939b8a21..4a0d122ab 100644 --- a/pr/436/docs/iroh_quinn_proto/struct.TransportError.html +++ b/pr/436/docs/iroh_quinn_proto/struct.TransportError.html @@ -11,7 +11,7 @@
§reason: String

Human-readable explanation of the reason

§crypto: Option<Arc<dyn Error + Send + Sync>>

An underlying crypto (e.g. TLS) layer error

Implementations§

Source§

impl Error

Source

pub fn new(code: Code, reason: String) -> Self

Construct an error with a code and a reason

-

Trait Implementations§

Source§

impl Clone for Error

Source§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for TransportError

Source§

fn from(e: Error) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Error

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +

Trait Implementations§

Source§

impl Clone for Error

Source§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for TransportError

Source§

fn from(e: Error) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Error

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl !RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl !UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where diff --git a/pr/436/docs/iroh_quinn_proto/trait.NetworkChangeHint.html b/pr/436/docs/iroh_quinn_proto/trait.NetworkChangeHint.html index 2050d3cfd..fe52d5fba 100644 --- a/pr/436/docs/iroh_quinn_proto/trait.NetworkChangeHint.html +++ b/pr/436/docs/iroh_quinn_proto/trait.NetworkChangeHint.html @@ -1,4 +1,4 @@ -NetworkChangeHint in iroh_quinn_proto - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
+NetworkChangeHint in iroh_quinn_proto - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
     // Required method
     fn is_path_recoverable(
         &self,
@@ -6,7 +6,7 @@
         network_path: FourTuple,
     ) -> bool;
 }
Expand description

Hints when the caller identifies a network change.

-

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

+

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

After network changes, paths may not be recoverable. In this case, waiting for the path to become idle may take longer than what is desirable. If Self::is_path_recoverable returns false, a multipath-enabled, client-side connection will establish a new path to diff --git a/pr/436/docs/search.index/entry/3daf0570cb59.js b/pr/436/docs/search.index/entry/afdfd6a4eeed.js similarity index 94% rename from pr/436/docs/search.index/entry/3daf0570cb59.js rename to pr/436/docs/search.index/entry/afdfd6a4eeed.js index b18dc92d2..b0add863f 100644 --- a/pr/436/docs/search.index/entry/3daf0570cb59.js +++ b/pr/436/docs/search.index/entry/afdfd6a4eeed.js @@ -1 +1 @@ -rd_("Ah[3974,15,3975,0,121,0,0]Ak[4761,15,4762,4762,122,0,0]Ai[3974,15,3975,0,4161,0,0]Al[4761,15,4762,1945,4162,0,0]Al[4465,15,4466,4466,4163,0,0]Al[4761,15,1851,1851,1603,0,0]Ao[1374,13,1375,1375,4006,3157,0]Ao[3974,13,3975,3730,4805,3157,0]An[3974,13,3975,1057,482,3157,0]Ao[3974,13,3975,1057,4431,3157,0]Ao[3974,13,3975,4110,4424,3157,0]Ao[3974,13,3975,4110,3165,3157,0]Ao[3974,13,3975,4110,3352,3157,0]Ao[3974,13,3975,4110,4423,3157,0]Ao[3974,13,3975,4115,3370,3157,0]Ao[3974,13,3975,4115,4195,3157,0]Al[3974,13,3975,0,1613,3157,0]Al[3974,13,3975,0,4964,3157,0]Al[3974,13,3975,0,1333,3157,0]Al[3974,13,3975,0,1596,3157,0]Al[3974,13,3975,0,2448,3157,0]Al[3974,13,3975,0,3162,3157,0]Al[3974,13,3975,0,2030,3157,0]Al[3974,13,3975,0,4147,3157,0]Al[3974,13,3975,0,4519,3157,0]Al[3974,13,3975,0,4019,3157,0]Al[3974,13,3975,0,4009,3157,0]Al[3974,13,3975,0,3145,3157,0]Al[3974,13,3975,0,4516,3157,0]Al[3974,13,3975,0,4706,3157,0]Al[3974,13,3975,0,4153,3157,0]Al[3974,13,3975,0,4156,3157,0]Al[3974,13,3975,0,4161,3157,0]Al[3974,13,3975,0,4863,3157,0]Ak[3974,13,3975,0,489,3157,0]Ak[3974,13,3975,0,121,3157,0]Al[3974,13,3975,0,2436,3157,0]Al[3974,13,1855,0,5112,3157,0]Al[3974,13,1934,0,1350,3157,0]Ao[4761,13,4762,1971,1614,3157,0]Ao[4761,13,4762,1971,4965,3157,0]Ao[4761,13,4762,3181,1334,3157,0]Ao[4761,13,4762,3190,4806,3157,0]Ao[4761,13,4762,1537,1597,3157,0]Ao[4761,13,4762,1537,3346,3157,0]Ao[4761,13,4762,1537,3161,3157,0]Ao[4761,13,4762,1537,4860,3157,0]Ao[4761,13,4762,1537,3321,3157,0]Ao[4761,13,4762,1553,2449,3157,0]Ao[4761,13,4762,1553,3163,3157,0]Ao[4761,13,4762,1083,3166,3157,0]Ao[4761,13,4762,1083,4315,3157,0]Ao[4761,13,4762,1088,2031,3157,0]Ao[4761,13,4762,1088,3371,3157,0]Ao[4761,13,4762,1088,4018,3157,0]Ao[4761,13,4762,2164,4036,3157,0]Ao[4761,13,4762,2164,4425,3157,0]Ao[4761,13,4762,2164,4151,3157,0]Ao[4761,13,4762,3731,4520,3157,0]Ao[4761,13,4762,3731,3158,3157,0]Ao[4761,13,4762,3731,4391,3157,0]Ao[4761,13,4762,3308,4020,3157,0]Ao[4761,13,4762,1853,4010,3157,0]Ao[4761,13,4762,1527,3146,3157,0]Ao[4761,13,4762,1527,4406,3157,0]Ao[4761,13,4762,1527,3338,3157,0]Ao[4761,13,4762,1527,3360,3157,0]Ao[4761,13,4762,1527,4158,3157,0]Ao[4761,13,4762,1527,4518,3157,0]Ao[4761,13,4762,1527,4708,3157,0]Ao[4761,13,4762,2459,4712,3157,0]Ao[4761,13,4762,2459,4154,3157,0]Ao[4761,13,4762,1918,2420,3157,0]Ao[4761,13,4762,1918,4804,3157,0]Ao[4761,13,4762,1945,4157,3157,0]Ao[4761,13,4762,1945,4162,3157,0]Al[4761,13,4762,0,4429,3157,0]Al[4761,13,4762,0,4864,3157,0]An[4761,13,4762,4762,490,3157,0]An[4761,13,4762,4762,122,3157,0]Ao[4761,13,4762,4762,2437,3157,0]Ao[4761,13,4762,4762,3144,3157,0]Ao[4761,13,1851,1851,4321,3157,0]Ao[4761,13,5006,5006,4905,3157,0]Ao[4761,13,5006,5006,1352,3157,0]Ao[4761,13,1856,1856,5113,3157,0]Al[4761,13,1935,0,1351,3157,0]Ao[4465,13,4466,4466,4163,3157,0]Ak[3974,13,3975,1057,482,0,0]Al[3974,13,3975,1057,4431,0,0]Al[3974,13,3975,4110,3350,0,0]Al[3974,13,3975,4110,4906,0,0]Al[3974,13,3975,4115,3356,0,0]Ai[4761,14,3159,0,1590,0,0]Ai[4761,14,3159,0,3125,0,0]Ai[4761,14,3159,0,4298,0,0]Ai[4761,14,3159,0,4182,0,0]Ai[4761,14,3159,0,4175,0,0]Ai[4761,14,4035,0,2430,0,0]Ai[4761,14,4035,0,2451,0,0]Ai[4761,14,4035,0,2415,0,0]Ai[4761,14,4035,0,2018,0,0]Ae[1374,7,1375,0,0,0,0]Ah[4761,14,4531,0,478,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ag[3974,5,3725,137,0,0,0]Ag[4761,5,3726,137,0,0,0]Al[1058,15,1059,1059,4899,0,0]Ae[3994,5,3995,0,0,0,0]Ah[3974,6,3975,4762,0,0,0]Ae[4761,6,4762,0,0,0,0]Al[4761,15,1851,1851,1603,0,0]Ai[3974,19,3975,0,1613,0,0]Ai[3974,19,3975,0,1596,0,0]Al[4761,19,4762,1971,1614,0,0]Al[4761,19,4762,1537,1597,0,0]Ae[1374,5,1375,0,0,0,0]Ae[1058,5,1845,0,0,0,0]Ae[1058,5,1940,0,0,0,0]Ah[3974,15,3975,0,121,0,0]Ak[4761,15,4762,4762,122,0,0]Ak[3974,13,3975,0,1613,132,0]Ak[3974,13,3975,0,1596,132,0]Ak[3974,13,3975,0,4019,132,0]Ak[3974,13,3975,0,4156,132,0]Aj[3974,13,3975,0,489,132,0]Aj[3974,13,3975,0,121,132,0]Ak[3974,13,3975,0,2436,132,0]An[4761,13,4762,1971,1614,132,0]An[4761,13,4762,3190,4806,132,0]An[4761,13,4762,1537,1597,132,0]An[4761,13,4762,1083,3166,132,0]An[4761,13,4762,1083,4315,132,0]An[4761,13,4762,1088,3371,132,0]An[4761,13,4762,3308,4020,132,0]An[4761,13,4762,2459,4712,132,0]An[4761,13,4762,1918,4804,132,0]An[4761,13,4762,1945,4157,132,0]Am[4761,13,4762,4762,490,132,0]Am[4761,13,4762,4762,122,132,0]An[4761,13,4762,4762,2437,132,0]Ai[3974,13,3975,0,2436,0,0]Al[4761,13,4762,4762,2437,0,0]Ai[4761,14,4035,0,1591,0,0]Ai[4761,14,4035,0,3131,0,0]Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Al[4465,14,4466,4466,2434,0,0]Al[4465,14,4466,4466,2447,0,0]An[1374,13,1375,1375,129,1340,0]Ao[1374,13,1375,1375,4006,1340,0]Ao[1374,13,1551,1551,4426,1340,0]Ao[4043,13,4044,4044,5054,1340,0]Ao[3974,13,3975,3730,3323,1340,0]Ao[3974,13,3975,3730,3325,1340,0]Ao[3974,13,3975,3730,4967,1340,0]Db[3974,13,3975,3730,4805,1340,0,\"impl-Debug-for-SendDatagramError\"]Dd[3974,13,3975,3730,4805,1990,0,\"impl-Display-for-SendDatagramError\"]Ao[3974,13,3975,2458,2413,1340,0]Ao[3974,13,3975,2458,4288,1340,0]Ao[3974,13,3975,2485,2417,1340,0]Ck[3974,13,3975,2485,3353,1340,0,\"impl-Debug-for-RetryError\"]Cm[3974,13,3975,2485,3353,1990,0,\"impl-Display-for-RetryError\"]Ao[3974,13,3975,2485,4405,1340,0]An[3974,13,3975,1057,482,1340,0]Ao[3974,13,3975,1057,4431,1340,0]Ao[3974,13,3975,4110,3350,1340,0]Ao[3974,13,3975,4110,4906,1340,0]Da[3974,13,3975,4110,4424,1990,0,\"impl-Display-for-ReadToEndError\"]Co[3974,13,3975,4110,4424,1340,0,\"impl-Debug-for-ReadToEndError\"]Cl[3974,13,3975,4110,3165,1990,0,\"impl-Display-for-ReadError\"]Cj[3974,13,3975,4110,3165,1340,0,\"impl-Debug-for-ReadError\"]Cm[3974,13,3975,4110,3352,1990,0,\"impl-Display-for-ResetError\"]Ck[3974,13,3975,4110,3352,1340,0,\"impl-Debug-for-ResetError\"]Co[3974,13,3975,4110,4423,1340,0,\"impl-Debug-for-ReadExactError\"]Da[3974,13,3975,4110,4423,1990,0,\"impl-Display-for-ReadExactError\"]Ao[3974,13,3975,1564,4196,1340,0]Ao[3974,13,3975,1100,4032,1340,0]Ao[3974,13,3975,4115,3356,1340,0]Cm[3974,13,3975,4115,3370,1990,0,\"impl-Display-for-WriteError\"]Ck[3974,13,3975,4115,3370,1340,0,\"impl-Debug-for-WriteError\"]Co[3974,13,3975,4115,4195,1990,0,\"impl-Display-for-StoppedError\"]Cm[3974,13,3975,4115,4195,1340,0,\"impl-Debug-for-StoppedError\"]Ao[3974,13,3975,4115,2020,1340,0]Cd[3974,13,3975,0,1613,1340,0,\"impl-Debug-for-VarInt\"]Cf[3974,13,3975,0,1613,1990,0,\"impl-Display-for-VarInt\"]Dd[3974,13,3975,0,4964,1990,0,\"impl-Display-for-VarIntBoundsExceeded\"]Db[3974,13,3975,0,4964,1340,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Al[3974,13,3975,0,1333,1340,0]Cf[3974,13,3975,0,1596,1990,0,\"impl-Display-for-PathId\"]Cd[3974,13,3975,0,1596,1340,0,\"impl-Debug-for-PathId\"]Al[3974,13,3975,0,2448,1340,0]Al[3974,13,3975,0,3331,1340,0]Al[3974,13,3975,0,3162,1340,0]Al[3974,13,3975,0,4523,1340,0]Al[3974,13,3975,0,2030,1340,0]Cl[3974,13,3975,0,4147,1990,0,\"impl-Display-for-ClosedStream\"]Cj[3974,13,3975,0,4147,1340,0,\"impl-Debug-for-ClosedStream\"]Co[3974,13,3975,0,4519,1990,0,\"impl-Display-for-ConnectionError\"]Cm[3974,13,3975,0,4519,1340,0,\"impl-Debug-for-ConnectionError\"]Al[3974,13,3975,0,4533,1340,0]Al[3974,13,3975,0,4540,1340,0]Al[3974,13,3975,0,4829,1340,0]Al[3974,13,3975,0,4844,1340,0]Al[3974,13,3975,0,4019,1340,0]Al[3974,13,3975,0,4400,1340,0]Al[3974,13,3975,0,4189,1340,0]Al[3974,13,3975,0,5021,1340,0]Al[3974,13,3975,0,4141,1340,0]Ck[3974,13,3975,0,4009,1990,0,\"impl-Display-for-ConfigError\"]Ci[3974,13,3975,0,4009,1340,0,\"impl-Debug-for-ConfigError\"]Ci[3974,13,3975,0,3145,1990,0,\"impl-Display-for-FrameType\"]Cg[3974,13,3975,0,3145,1340,0,\"impl-Debug-for-FrameType\"]Cm[3974,13,3975,0,4516,1340,0,\"impl-Debug-for-ConnectionClose\"]Co[3974,13,3975,0,4516,1990,0,\"impl-Display-for-ConnectionClose\"]Cn[3974,13,3975,0,4706,1340,0,\"impl-Debug-for-ApplicationClose\"]D`[3974,13,3975,0,4706,1990,0,\"impl-Display-for-ApplicationClose\"]Cl[3974,13,3975,0,4153,1990,0,\"impl-Display-for-ConnectError\"]Cj[3974,13,3975,0,4153,1340,0,\"impl-Debug-for-ConnectError\"]Cj[3974,13,3975,0,4156,1340,0,\"impl-Debug-for-ConnectionId\"]Cl[3974,13,3975,0,4156,1990,0,\"impl-Display-for-ConnectionId\"]Al[3974,13,3975,0,4161,1340,0]Db[3974,13,3975,0,4863,1990,0,\"impl-Display-for-TransportErrorCode\"]D`[3974,13,3975,0,4863,1340,0,\"impl-Debug-for-TransportErrorCode\"]Al[3974,13,3975,0,3334,1340,0]Al[3974,13,3975,0,4737,1340,0]Ak[3974,13,3975,0,489,1340,0]C`[3974,13,3975,0,121,1340,0,\"impl-Debug-for-Dir\"]Cb[3974,13,3975,0,121,1990,0,\"impl-Display-for-Dir\"]Ch[3974,13,3975,0,2436,1990,0,\"impl-Display-for-StreamId\"]Cf[3974,13,3975,0,2436,1340,0,\"impl-Debug-for-StreamId\"]Al[3974,13,3975,0,2445,1340,0]Ak[3974,13,3725,0,115,1340,0]Al[3974,13,3725,0,3134,1340,0]Al[3974,13,3725,0,1337,1340,0]Al[3974,13,3725,0,4014,1340,0]Al[3974,13,3725,0,2003,1340,0]Al[3974,13,3725,0,4307,1340,0]Al[3974,13,3725,0,4795,1340,0]Al[3974,13,1855,0,5112,1340,0]Al[3974,13,1855,0,4012,1340,0]Al[3974,13,1855,0,4867,1340,0]Db[3974,13,1934,0,4960,1340,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dd[3974,13,1934,0,4960,1990,0,\"impl-Display-for-NoInitialCipherSuite\"]Cc[3974,13,1934,0,1350,1340,0,\"impl-Debug-for-Error\"]Ce[3974,13,1934,0,1350,1990,0,\"impl-Display-for-Error\"]Ao[4761,13,4762,3731,3326,1340,0]Ao[4761,13,4762,2459,2414,1340,0]Ci[4761,13,4762,1971,1614,1990,0,\"impl-Display-for-VarInt\"]Cg[4761,13,4762,1971,1614,1340,0,\"impl-Debug-for-VarInt\"]De[4761,13,4762,1971,4965,1340,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Dg[4761,13,4762,1971,4965,1990,0,\"impl-Display-for-VarIntBoundsExceeded\"]Ao[4761,13,4762,3181,1334,1340,0]Dd[4761,13,4762,3190,4806,1990,0,\"impl-Display-for-SendDatagramError\"]Db[4761,13,4762,3190,4806,1340,0,\"impl-Debug-for-SendDatagramError\"]Ci[4761,13,4762,1537,1597,1990,0,\"impl-Display-for-PathId\"]Cg[4761,13,4762,1537,1597,1340,0,\"impl-Debug-for-PathId\"]Ao[4761,13,4762,1537,4185,1340,0]Ao[4761,13,4762,1537,3346,1340,0]Ao[4761,13,4762,1537,3161,1340,0]Dc[4761,13,4762,1537,4860,1340,0,\"impl-Debug-for-SetPathStatusError\"]De[4761,13,4762,1537,4860,1990,0,\"impl-Display-for-SetPathStatusError\"]Ck[4761,13,4762,1537,3321,1340,0,\"impl-Debug-for-ClosedPath\"]Cm[4761,13,4762,1537,3321,1990,0,\"impl-Display-for-ClosedPath\"]Ao[4761,13,4762,1553,2449,1340,0]Ao[4761,13,4762,1553,3332,1340,0]Ao[4761,13,4762,1553,3163,1340,0]Ao[4761,13,4762,1553,4524,1340,0]Cl[4761,13,4762,1083,3166,1990,0,\"impl-Display-for-ReadError\"]Cj[4761,13,4762,1083,3166,1340,0,\"impl-Debug-for-ReadError\"]D`[4761,13,4762,1083,4315,1990,0,\"impl-Display-for-ReadableError\"]Cn[4761,13,4762,1083,4315,1340,0,\"impl-Debug-for-ReadableError\"]Ao[4761,13,4762,1088,2031,1340,0]Cm[4761,13,4762,1088,3371,1990,0,\"impl-Display-for-WriteError\"]Ck[4761,13,4762,1088,3371,1340,0,\"impl-Debug-for-WriteError\"]Cl[4761,13,4762,1088,4018,1340,0,\"impl-Debug-for-FinishError\"]Cn[4761,13,4762,1088,4018,1990,0,\"impl-Display-for-FinishError\"]Ao[4761,13,4762,2164,4036,1340,0]Ao[4761,13,4762,2164,4425,1340,0]Co[4761,13,4762,2164,4151,1990,0,\"impl-Display-for-ClosedStream\"]Cm[4761,13,4762,2164,4151,1340,0,\"impl-Debug-for-ClosedStream\"]D`[4761,13,4762,3731,4520,1340,0,\"impl-Debug-for-ConnectionError\"]Db[4761,13,4762,3731,4520,1990,0,\"impl-Display-for-ConnectionError\"]Cl[4761,13,4762,3731,3158,1990,0,\"impl-Display-for-PathError\"]Cj[4761,13,4762,3731,3158,1340,0,\"impl-Debug-for-PathError\"]Da[4761,13,4762,3731,4391,1990,0,\"impl-Display-for-ClosePathError\"]Co[4761,13,4762,3731,4391,1340,0,\"impl-Debug-for-ClosePathError\"]Di[4761,13,4762,3731,5050,1990,0,\"impl-Display-for-MultipathNotNegotiated\"]Dg[4761,13,4762,3731,5050,1340,0,\"impl-Debug-for-MultipathNotNegotiated\"]Ao[4761,13,4762,3731,1355,1340,0]Ao[4761,13,4762,1079,4534,1340,0]Ao[4761,13,4762,3308,4541,1340,0]Ao[4761,13,4762,3308,4830,1340,0]Ao[4761,13,4762,3308,4845,1340,0]Ao[4761,13,4762,3308,4020,1340,0]Ao[4761,13,4762,1853,4401,1340,0]Ao[4761,13,4762,1853,4191,1340,0]Ao[4761,13,4762,1853,5022,1340,0]Ao[4761,13,4762,1853,4143,1340,0]Cn[4761,13,4762,1853,4010,1990,0,\"impl-Display-for-ConfigError\"]Cl[4761,13,4762,1853,4010,1340,0,\"impl-Debug-for-ConfigError\"]Cl[4761,13,4762,1527,3146,1990,0,\"impl-Display-for-FrameType\"]Cj[4761,13,4762,1527,3146,1340,0,\"impl-Debug-for-FrameType\"]Da[4761,13,4762,1527,4406,1990,0,\"impl-Display-for-InvalidFrameId\"]Co[4761,13,4762,1527,4406,1340,0,\"impl-Debug-for-InvalidFrameId\"]Cm[4761,13,4762,1527,3338,1990,0,\"impl-Display-for-MaybeFrame\"]Ck[4761,13,4762,1527,3338,1340,0,\"impl-Debug-for-MaybeFrame\"]Ck[4761,13,4762,1527,3360,1340,0,\"impl-Debug-for-StreamInfo\"]Cm[4761,13,4762,1527,3360,1990,0,\"impl-Display-for-StreamInfo\"]Co[4761,13,4762,1527,4158,1990,0,\"impl-Display-for-DatagramInfo\"]Cm[4761,13,4762,1527,4158,1340,0,\"impl-Debug-for-DatagramInfo\"]Db[4761,13,4762,1527,4518,1990,0,\"impl-Display-for-ConnectionClose\"]D`[4761,13,4762,1527,4518,1340,0,\"impl-Debug-for-ConnectionClose\"]Da[4761,13,4762,1527,4708,1340,0,\"impl-Debug-for-ApplicationClose\"]Dc[4761,13,4762,1527,4708,1990,0,\"impl-Display-for-ApplicationClose\"]Ci[4761,13,4762,1527,2407,1340,0,\"impl-Debug-for-Datagram\"]Ck[4761,13,4762,1527,2407,1990,0,\"impl-Display-for-Datagram\"]Ao[4761,13,4762,2459,4712,1340,0]Ao[4761,13,4762,2459,2418,1340,0]Co[4761,13,4762,2459,4154,1990,0,\"impl-Display-for-ConnectError\"]Cm[4761,13,4762,2459,4154,1340,0,\"impl-Debug-for-ConnectError\"]Ao[4761,13,4762,2459,4004,1340,0]Cm[4761,13,4762,2459,3354,1990,0,\"impl-Display-for-RetryError\"]Ck[4761,13,4762,2459,3354,1340,0,\"impl-Debug-for-RetryError\"]Ao[4761,13,4762,1918,4310,1340,0]Ao[4761,13,4762,1918,4532,1340,0]Ao[4761,13,4762,1918,5053,1340,0]Ao[4761,13,4762,1918,2420,1340,0]Db[4761,13,4762,1918,4804,1340,0,\"impl-Debug-for-PacketDecodeError\"]Dd[4761,13,4762,1918,4804,1990,0,\"impl-Display-for-PacketDecodeError\"]Ao[4761,13,4762,1945,4522,1340,0]Ao[4761,13,4762,1945,4287,1340,0]Cm[4761,13,4762,1945,4157,1340,0,\"impl-Debug-for-ConnectionId\"]Co[4761,13,4762,1945,4157,1990,0,\"impl-Display-for-ConnectionId\"]Ao[4761,13,4762,1945,4162,1340,0]Ce[4761,13,4762,0,4429,1990,0,\"impl-Display-for-Error\"]Cc[4761,13,4762,0,4429,1340,0,\"impl-Debug-for-Error\"]Cb[4761,13,4762,0,4864,1340,0,\"impl-Debug-for-Code\"]Cd[4761,13,4762,0,4864,1990,0,\"impl-Display-for-Code\"]Ao[4761,13,4762,4328,3335,1340,0]Ao[4761,13,4762,4328,5146,1340,0]Ao[4761,13,4762,4895,4738,1340,0]An[4761,13,4762,4762,490,1340,0]Ce[4761,13,4762,4762,122,1990,0,\"impl-Display-for-Dir\"]Cc[4761,13,4762,4762,122,1340,0,\"impl-Debug-for-Dir\"]Ck[4761,13,4762,4762,2437,1990,0,\"impl-Display-for-StreamId\"]Ci[4761,13,4762,4762,2437,1340,0,\"impl-Debug-for-StreamId\"]Ao[4761,13,4762,4762,2446,1340,0]Cl[4761,13,4762,4762,3144,1990,0,\"impl-Display-for-FourTuple\"]Cj[4761,13,4762,4762,3144,1340,0,\"impl-Debug-for-FourTuple\"]Cn[4761,13,1851,1851,4321,1340,0,\"impl-Debug-for-UnexpectedEnd\"]D`[4761,13,1851,1851,4321,1990,0,\"impl-Display-for-UnexpectedEnd\"]Ao[4761,13,5006,5006,4905,1340,0]Cf[4761,13,5006,5006,1352,1340,0,\"impl-Debug-for-Error\"]Ch[4761,13,5006,5006,1352,1990,0,\"impl-Display-for-Error\"]Ao[4761,13,1856,1856,5113,1340,0]Ao[4761,13,1856,1856,4013,1340,0]Ao[4761,13,1856,1856,4870,1340,0]De[4761,13,1935,1935,4961,1340,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dg[4761,13,1935,1935,4961,1990,0,\"impl-Display-for-NoInitialCipherSuite\"]Ce[4761,13,1935,0,1351,1990,0,\"impl-Display-for-Error\"]Cc[4761,13,1935,0,1351,1340,0,\"impl-Debug-for-Error\"]Am[4761,13,3726,137,116,1340,0]An[4761,13,3726,137,3135,1340,0]Ao[4761,13,3726,1522,1338,1340,0]Ao[4761,13,3726,1522,4015,1340,0]Ao[4761,13,3726,2507,2004,1340,0]Ao[4761,13,3726,2507,4308,1340,0]Ao[4761,13,3726,3726,4796,1340,0]Cf[4761,13,2135,2135,1353,1340,0,\"impl-Debug-for-Error\"]Ch[4761,13,2135,2135,1353,1990,0,\"impl-Display-for-Error\"]Ao[4761,13,2135,2135,1356,1340,0]An[4465,13,4466,397,4430,1340,0]Ao[4465,13,4466,4466,2434,1340,0]Ao[4465,13,4466,4466,2447,1340,0]Ao[4465,13,4466,4466,4163,1340,0]Al[4761,13,4762,1537,4185,0,0]Ai[3974,14,3975,0,2448,0,0]Al[4761,14,4762,1553,2449,0,0]Al[4761,13,4762,1918,4310,0,0]Al[4761,14,4762,1918,5053,0,0]Ah[4761,14,4531,0,478,0,0]Al[4465,14,4466,4466,2434,0,0]Ai[3974,13,3975,0,5021,0,0]Al[4761,13,4762,1853,5022,0,0]8Al[1374,13,1551,1551,4426,0,0]Al[4043,13,4044,4044,5054,0,0]Ai[3974,13,3975,0,4279,0,0]Al[3974,13,3975,2458,2413,0,0]Ai[3974,13,3975,0,3347,0,0]Ai[3974,13,3975,0,4533,0,0]Ai[3974,13,3975,0,4400,0,0]Ai[3974,13,3975,0,4189,0,0]Ai[3974,13,3975,0,4141,0,0]Ai[3974,13,3975,0,4156,0,0]Ai[3974,13,3975,0,4737,0,0]Ai[3974,13,3975,0,2436,0,0]Ah[3974,13,3725,0,115,0,0]Ai[3974,13,3725,0,1337,0,0]Ai[3974,13,3725,0,2003,0,0]Al[4761,13,4762,4550,4280,0,0]Al[4761,13,4762,1079,3348,0,0]Al[4761,13,4762,2459,2414,0,0]Al[4761,13,4762,1918,5171,0,0]Al[4761,13,4762,4328,5141,0,0]Al[4761,13,4762,1079,4534,0,0]Al[4761,13,4762,1853,4401,0,0]Al[4761,13,4762,1853,4191,0,0]Al[4761,13,4762,1853,4143,0,0]Al[4761,13,4762,1918,4310,0,0]Al[4761,13,4762,1945,4157,0,0]Ai[4761,13,4762,0,4429,0,0]Al[4761,13,4762,4328,5146,0,0]Al[4761,13,4762,4895,4738,0,0]Al[4761,13,4762,4762,2437,0,0]Aj[4761,13,3726,137,116,0,0]Al[4761,13,3726,1522,1338,0,0]Al[4761,13,3726,2507,2004,0,0]Ak[4465,13,4466,397,4430,0,0]Al[1058,13,4248,4248,5098,0,0]Al[1058,13,4248,4248,5099,0,0]Aj[3974,13,3975,0,489,128,0]Am[4761,13,4762,4762,490,128,0]Ai[3974,12,3975,0,3363,0,0]Al[3974,13,3975,2156,2015,0,0]0Ao[3974,13,3975,1564,4196,2015,0]Al[3974,13,3975,0,4319,3363,0]Al[4761,12,4762,1853,3364,0,0]Ao[4761,13,4762,1853,4320,3364,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,13,4762,3731,3326,0,0]Al[4761,14,4762,1553,3163,0,0]Ae[1058,7,1845,0,0,0,0]Ae[1058,7,1940,0,0,0,0]Ae[3974,4,3975,0,0,0,0]Ai[3974,15,3975,0,4161,0,0]Al[4761,15,4762,1945,4162,0,0]Al[4465,15,4466,4466,4163,0,0]210Ah[3974,5,1855,1856,0,0,0]Ae[4761,5,1856,0,0,0,0]Al[4761,15,4762,1918,4532,0,0]Al[4761,15,4762,1527,3338,0,0]Ah[3974,5,3975,1057,0,0,0]Al[4761,15,4762,3731,1355,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Al[3974,15,3975,4110,4424,0,0]Ah[3974,6,3975,4762,0,0,0]Ae[4761,6,4762,0,0,0,0]Ai[3974,19,3975,0,1596,0,0]Al[4761,19,4762,1537,1597,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]Ai[4761,14,3159,0,4175,0,0]Al[4465,14,4466,4466,2434,0,0]Aa[499,3,0,0,0,0,0]Ai[4761,14,4762,0,4429,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1553,3163,0,0]Al[4761,14,4762,1527,2407,0,0]An[3974,13,3975,3730,2427,466,0]An[3974,13,3975,2485,2417,466,0]Am[3974,13,3975,1057,482,466,0]An[3974,13,3975,1057,4431,466,0]An[3974,13,3975,4110,3350,466,0]An[3974,13,3975,4115,3356,466,0]An[4761,13,4762,1083,1578,466,0]Am[1374,13,1375,1375,129,473,0]An[1374,13,1375,1375,4006,473,0]An[1374,13,1551,1551,4037,473,0]An[1374,13,1551,1551,1370,473,0]An[1374,13,1551,1551,4426,473,0]Ak[499,13,500,500,4007,473,0]An[4043,13,4044,4044,5054,473,0]An[3974,13,3975,3730,4545,473,0]An[3974,13,3975,3730,2427,473,0]Ak[3974,13,3975,0,4279,473,0]An[3974,13,3975,1057,2428,473,0]An[3974,13,3975,1057,4704,473,0]An[3974,13,3975,3730,3323,473,0]An[3974,13,3975,3730,3325,473,0]An[3974,13,3975,3730,2006,473,0]An[3974,13,3975,3730,1589,473,0]An[3974,13,3975,3730,3127,473,0]An[3974,13,3975,3730,2401,473,0]An[3974,13,3975,3730,4181,473,0]An[3974,13,3975,3730,4186,473,0]An[3974,13,3975,3730,4967,473,0]An[3974,13,3975,3730,4805,473,0]0An[3974,13,3975,2458,2413,473,0]An[3974,13,3975,2458,4288,473,0]An[3974,13,3975,2458,1570,473,0]An[3974,13,3975,2485,2417,473,0]An[3974,13,3975,2485,3353,473,0]An[3974,13,3975,2485,4405,473,0]Am[3974,13,3975,1057,482,473,0]An[3974,13,3975,1057,4431,473,0]An[3974,13,3975,4110,3350,473,0]An[3974,13,3975,4110,4906,473,0]An[3974,13,3975,4110,4424,473,0]0An[3974,13,3975,4110,3165,473,0]Dh[3974,13,3975,4110,3165,473,0,\"impl-From%3CResetError%3E-for-ReadError\"]Dm[3974,13,3975,4110,3165,473,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]An[3974,13,3975,4110,3352,473,0]0An[3974,13,3975,4110,4423,473,0]0An[3974,13,3975,1564,4196,473,0]An[3974,13,3975,1100,4032,473,0]An[3974,13,3975,4115,3356,473,0]Dk[3974,13,3975,4115,3370,473,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dn[3974,13,3975,4115,3370,473,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Dk[3974,13,3975,4115,3370,473,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]An[3974,13,3975,4115,3370,473,0]An[3974,13,3975,4115,4195,473,0]0An[3974,13,3975,4115,2020,473,0]Ak[3974,13,3975,0,3347,473,0]Ak[3974,13,3975,0,4319,473,0]Ak[3974,13,3975,0,4537,473,0]Ak[3974,13,3975,0,4173,473,0]Ak[3974,13,3975,0,4414,473,0]Cj[3974,13,3975,0,1613,473,0,\"impl-From%3Cu8%3E-for-VarInt\"]D`[3974,13,3975,0,1613,473,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Dj[3974,13,3975,0,1613,473,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Ak[3974,13,3975,0,1613,473,0]Ck[3974,13,3975,0,1613,473,0,\"impl-From%3Cu16%3E-for-VarInt\"]Ck[3974,13,3975,0,1613,473,0,\"impl-From%3Cu32%3E-for-VarInt\"]Ak[3974,13,3975,0,4964,473,0]Ak[3974,13,3975,0,1333,473,0]Ak[3974,13,3975,0,1596,473,0]0Ak[3974,13,3975,0,2448,473,0]Ak[3974,13,3975,0,3331,473,0]Ak[3974,13,3975,0,3162,473,0]Ak[3974,13,3975,0,4523,473,0]Ak[3974,13,3975,0,2030,473,0]Ak[3974,13,3975,0,4147,473,0]Ak[3974,13,3975,0,4519,473,0]Df[3974,13,3975,0,4519,473,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Dl[3974,13,3975,0,4519,473,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Do[3974,13,3975,0,4519,473,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ak[3974,13,3975,0,4533,473,0]Ak[3974,13,3975,0,4540,473,0]Ak[3974,13,3975,0,4829,473,0]Ak[3974,13,3975,0,4844,473,0]Ak[3974,13,3975,0,4019,473,0]0Ak[3974,13,3975,0,4400,473,0]Ak[3974,13,3975,0,4189,473,0]Ak[3974,13,3975,0,5021,473,0]Ak[3974,13,3975,0,4141,473,0]Ea[3974,13,3975,0,4009,473,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ak[3974,13,3975,0,4009,473,0]Dl[3974,13,3975,0,4009,473,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ak[3974,13,3975,0,3145,473,0]Ak[3974,13,3975,0,4516,473,0]0Ak[3974,13,3975,0,4706,473,0]Ak[3974,13,3975,0,4153,473,0]0Ak[3974,13,3975,0,4156,473,0]Ak[3974,13,3975,0,4161,473,0]Ak[3974,13,3975,0,4863,473,0]0Ak[3974,13,3975,0,3334,473,0]Ak[3974,13,3975,0,4737,473,0]Aj[3974,13,3975,0,489,473,0]Aj[3974,13,3975,0,121,473,0]Ak[3974,13,3975,0,2436,473,0]0Ak[3974,13,3975,0,2445,473,0]Aj[3974,13,3725,0,115,473,0]Ak[3974,13,3725,0,3134,473,0]Ak[3974,13,3725,0,1337,473,0]Ak[3974,13,3725,0,4014,473,0]Ak[3974,13,3725,0,2003,473,0]Ak[3974,13,3725,0,4307,473,0]Ak[3974,13,3725,0,4795,473,0]Ak[3974,13,1855,0,1998,473,0]Aj[3974,13,1855,0,476,473,0]Ak[3974,13,1855,0,5112,473,0]Ak[3974,13,1855,0,4012,473,0]0Ak[3974,13,1855,0,4867,473,0]Ak[3974,13,1934,0,3365,473,0]Ak[3974,13,1934,0,4290,473,0]Ak[3974,13,1934,0,4733,473,0]Ak[3974,13,1934,0,4735,473,0]Ak[3974,13,1934,0,4960,473,0]Df[3974,13,1934,0,1350,473,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]E`[3974,13,1934,0,1350,473,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]E`[3974,13,1934,0,1350,473,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Da[3974,13,1934,0,1350,473,0,\"impl-From%3COtherError%3E-for-Error\"]De[3974,13,1934,0,1350,473,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dg[3974,13,1934,0,1350,473,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]De[3974,13,1934,0,1350,473,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dg[3974,13,1934,0,1350,473,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dg[3974,13,1934,0,1350,473,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Df[3974,13,1934,0,1350,473,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Ak[3974,13,1934,0,1350,473,0]Dn[3974,13,1934,0,1350,473,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]An[4761,13,4762,4550,4280,473,0]An[4761,13,4762,3190,3140,473,0]An[4761,13,4762,1083,1578,473,0]An[4761,13,4762,2164,2024,473,0]An[4761,13,4762,2164,3351,473,0]An[4761,13,4762,2164,3357,473,0]An[4761,13,4762,3731,3326,473,0]An[4761,13,4762,1079,3348,473,0]An[4761,13,4762,1853,4320,473,0]An[4761,13,4762,2459,2414,473,0]An[4761,13,4762,2459,4286,473,0]An[4761,13,4762,1918,5171,473,0]An[4761,13,4762,4328,5141,473,0]An[4761,13,4762,1563,4538,473,0]An[4761,13,4762,1563,4174,473,0]An[4761,13,4762,1563,4415,473,0]Cm[4761,13,4762,1971,1614,473,0,\"impl-From%3Cu8%3E-for-VarInt\"]Cn[4761,13,4762,1971,1614,473,0,\"impl-From%3Cu32%3E-for-VarInt\"]Co[4761,13,4762,1971,1614,473,0,\"impl-From%3CCode%3E-for-VarInt\"]Cn[4761,13,4762,1971,1614,473,0,\"impl-From%3Cu16%3E-for-VarInt\"]An[4761,13,4762,1971,1614,473,0]Dc[4761,13,4762,1971,1614,473,0,\"impl-From%3CStreamId%3E-for-VarInt\"]An[4761,13,4762,1971,4965,473,0]An[4761,13,4762,3181,1334,473,0]An[4761,13,4762,3190,4806,473,0]An[4761,13,4762,1537,1597,473,0]0An[4761,13,4762,1537,4185,473,0]An[4761,13,4762,1537,3346,473,0]An[4761,13,4762,1537,3161,473,0]An[4761,13,4762,1537,4860,473,0]An[4761,13,4762,1537,3321,473,0]An[4761,13,4762,1553,2449,473,0]An[4761,13,4762,1553,3332,473,0]An[4761,13,4762,1553,3163,473,0]An[4761,13,4762,1553,4524,473,0]An[4761,13,4762,1083,3166,473,0]An[4761,13,4762,1083,4315,473,0]An[4761,13,4762,1088,2031,473,0]An[4761,13,4762,1088,3371,473,0]An[4761,13,4762,1088,4018,473,0]An[4761,13,4762,2164,4036,473,0]An[4761,13,4762,2164,4425,473,0]An[4761,13,4762,2164,4151,473,0]An[4761,13,4762,3731,4520,473,0]0An[4761,13,4762,3731,3158,473,0]An[4761,13,4762,3731,4391,473,0]An[4761,13,4762,3731,5050,473,0]An[4761,13,4762,3731,1355,473,0]0An[4761,13,4762,1079,4534,473,0]An[4761,13,4762,3308,4541,473,0]An[4761,13,4762,3308,4830,473,0]An[4761,13,4762,3308,4845,473,0]An[4761,13,4762,3308,4020,473,0]0An[4761,13,4762,1853,4401,473,0]An[4761,13,4762,1853,4191,473,0]An[4761,13,4762,1853,5022,473,0]An[4761,13,4762,1853,4143,473,0]Ed[4761,13,4762,1853,4010,473,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Do[4761,13,4762,1853,4010,473,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]An[4761,13,4762,1853,4010,473,0]An[4761,13,4762,1527,3146,473,0]An[4761,13,4762,1527,4406,473,0]An[4761,13,4762,1527,3338,473,0]An[4761,13,4762,1527,3360,473,0]An[4761,13,4762,1527,4158,473,0]An[4761,13,4762,1527,4518,473,0]0An[4761,13,4762,1527,4708,473,0]An[4761,13,4762,1527,2407,473,0]An[4761,13,4762,2459,4712,473,0]An[4761,13,4762,2459,2418,473,0]An[4761,13,4762,2459,4154,473,0]0An[4761,13,4762,2459,4004,473,0]An[4761,13,4762,2459,3354,473,0]An[4761,13,4762,1918,4310,473,0]An[4761,13,4762,1918,4532,473,0]An[4761,13,4762,1918,5053,473,0]An[4761,13,4762,1918,2420,473,0]An[4761,13,4762,1918,4804,473,0]0An[4761,13,4762,1945,4522,473,0]An[4761,13,4762,1945,4287,473,0]An[4761,13,4762,1945,4157,473,0]An[4761,13,4762,1945,4162,473,0]Ak[4761,13,4762,0,4429,473,0]0Ak[4761,13,4762,0,4864,473,0]0An[4761,13,4762,4328,3335,473,0]An[4761,13,4762,4328,5146,473,0]An[4761,13,4762,4895,4738,473,0]Am[4761,13,4762,4762,490,473,0]Am[4761,13,4762,4762,122,473,0]An[4761,13,4762,4762,2437,473,0]0An[4761,13,4762,4762,2446,473,0]An[4761,13,4762,4762,3144,473,0]An[4761,13,1851,1851,4321,473,0]An[4761,13,5006,5006,4905,473,0]An[4761,13,5006,5006,1352,473,0]0An[4761,13,1856,1856,1999,473,0]Am[4761,13,1856,1856,477,473,0]An[4761,13,1856,1856,5113,473,0]An[4761,13,1856,1856,4013,473,0]0An[4761,13,1856,1856,4870,473,0]An[4761,13,1935,1935,3366,473,0]An[4761,13,1935,1935,4291,473,0]An[4761,13,1935,1935,4734,473,0]An[4761,13,1935,1935,4736,473,0]An[4761,13,1935,1935,4961,473,0]Dg[4761,13,1935,0,1351,473,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]E`[4761,13,1935,0,1351,473,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Dg[4761,13,1935,0,1351,473,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Dn[4761,13,1935,0,1351,473,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]De[4761,13,1935,0,1351,473,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[4761,13,1935,0,1351,473,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]E`[4761,13,1935,0,1351,473,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dg[4761,13,1935,0,1351,473,0,\"impl-From%3CCertificateError%3E-for-Error\"]Ak[4761,13,1935,0,1351,473,0]Df[4761,13,1935,0,1351,473,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[4761,13,1935,0,1351,473,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Da[4761,13,1935,0,1351,473,0,\"impl-From%3COtherError%3E-for-Error\"]Al[4761,13,3726,137,116,473,0]Am[4761,13,3726,137,3135,473,0]An[4761,13,3726,1522,1338,473,0]An[4761,13,3726,1522,4015,473,0]An[4761,13,3726,2507,2004,473,0]An[4761,13,3726,2507,4308,473,0]An[4761,13,3726,3726,4796,473,0]An[4761,13,2135,2135,1353,473,0]An[4761,13,2135,2135,1356,473,0]An[4465,13,4466,4466,3369,473,0]0Am[4465,13,4466,397,4430,473,0]An[4465,13,4466,4466,2434,473,0]An[4465,13,4466,4466,2447,473,0]An[4465,13,4466,4466,4163,473,0]An[1058,13,1059,1059,3137,473,0]An[1058,13,1059,1059,4899,473,0]Am[1058,13,1845,1845,130,473,0]An[1058,13,1552,1552,1371,473,0]An[1058,13,1552,1552,4038,473,0]An[1058,13,1552,1552,4528,473,0]An[1058,13,4248,4248,5098,473,0]An[1058,13,4248,4248,5099,473,0]Am[1058,13,1940,1940,131,473,0]Am[3994,13,3995,3995,120,473,0]An[3994,13,3995,3995,2404,473,0]Ak[3974,13,3975,0,1613,474,0]Ak[3974,13,3975,0,1596,474,0]Ak[3974,13,3975,0,4019,474,0]Ak[3974,13,3975,0,4156,474,0]Aj[3974,13,3975,0,489,474,0]Aj[3974,13,3975,0,121,474,0]Ak[3974,13,3975,0,2436,474,0]An[4761,13,4762,1971,1614,474,0]An[4761,13,4762,3190,4806,474,0]An[4761,13,4762,1537,1597,474,0]An[4761,13,4762,1083,3166,474,0]An[4761,13,4762,1083,4315,474,0]An[4761,13,4762,1088,3371,474,0]An[4761,13,4762,3308,4020,474,0]An[4761,13,4762,2459,4712,474,0]An[4761,13,4762,1918,4804,474,0]An[4761,13,4762,1945,4157,474,0]Am[4761,13,4762,4762,490,474,0]Am[4761,13,4762,4762,122,474,0]An[4761,13,4762,4762,2437,474,0]An[4761,13,4762,4762,3144,474,0]Am[1374,13,1375,1375,129,475,0]An[1374,13,1375,1375,4006,475,0]An[1374,13,1551,1551,4037,475,0]An[1374,13,1551,1551,1370,475,0]An[1374,13,1551,1551,4426,475,0]Ak[499,13,500,500,4007,475,0]An[4043,13,4044,4044,5054,475,0]An[3974,13,3975,3730,4545,475,0]An[3974,13,3975,3730,2427,475,0]Ak[3974,13,3975,0,4279,475,0]An[3974,13,3975,1057,2428,475,0]An[3974,13,3975,1057,4704,475,0]An[3974,13,3975,3730,3323,475,0]An[3974,13,3975,3730,3325,475,0]An[3974,13,3975,3730,2006,475,0]An[3974,13,3975,3730,1589,475,0]An[3974,13,3975,3730,3127,475,0]An[3974,13,3975,3730,2401,475,0]An[3974,13,3975,3730,4181,475,0]An[3974,13,3975,3730,4186,475,0]An[3974,13,3975,3730,4967,475,0]An[3974,13,3975,3730,4805,475,0]An[3974,13,3975,2458,2413,475,0]An[3974,13,3975,2458,4288,475,0]An[3974,13,3975,2458,1570,475,0]An[3974,13,3975,2485,2417,475,0]An[3974,13,3975,2485,3353,475,0]An[3974,13,3975,2485,4405,475,0]Am[3974,13,3975,1057,482,475,0]An[3974,13,3975,1057,4431,475,0]An[3974,13,3975,4110,3350,475,0]An[3974,13,3975,4110,4906,475,0]An[3974,13,3975,4110,4424,475,0]An[3974,13,3975,4110,3165,475,0]An[3974,13,3975,4110,3352,475,0]An[3974,13,3975,4110,4423,475,0]An[3974,13,3975,1564,4196,475,0]An[3974,13,3975,1100,4032,475,0]An[3974,13,3975,4115,3356,475,0]An[3974,13,3975,4115,3370,475,0]An[3974,13,3975,4115,4195,475,0]An[3974,13,3975,4115,2020,475,0]Ak[3974,13,3975,0,3347,475,0]Ak[3974,13,3975,0,4319,475,0]Ak[3974,13,3975,0,4537,475,0]Ak[3974,13,3975,0,4173,475,0]Ak[3974,13,3975,0,4414,475,0]Ak[3974,13,3975,0,1613,475,0]Ak[3974,13,3975,0,4964,475,0]Ak[3974,13,3975,0,1333,475,0]Ak[3974,13,3975,0,1596,475,0]Ak[3974,13,3975,0,2448,475,0]Ak[3974,13,3975,0,3331,475,0]Ak[3974,13,3975,0,3162,475,0]Ak[3974,13,3975,0,4523,475,0]Ak[3974,13,3975,0,2030,475,0]Ak[3974,13,3975,0,4147,475,0]Ak[3974,13,3975,0,4519,475,0]Ak[3974,13,3975,0,4533,475,0]Ak[3974,13,3975,0,4540,475,0]Ak[3974,13,3975,0,4829,475,0]Ak[3974,13,3975,0,4844,475,0]Ak[3974,13,3975,0,4019,475,0]Ak[3974,13,3975,0,4400,475,0]Ak[3974,13,3975,0,4189,475,0]Ak[3974,13,3975,0,5021,475,0]Ak[3974,13,3975,0,4141,475,0]Ak[3974,13,3975,0,4009,475,0]Ak[3974,13,3975,0,3145,475,0]Ak[3974,13,3975,0,4516,475,0]Ak[3974,13,3975,0,4706,475,0]Ak[3974,13,3975,0,4153,475,0]Ak[3974,13,3975,0,4156,475,0]Ak[3974,13,3975,0,4161,475,0]Ak[3974,13,3975,0,4863,475,0]Ak[3974,13,3975,0,3334,475,0]Ak[3974,13,3975,0,4737,475,0]Aj[3974,13,3975,0,489,475,0]Aj[3974,13,3975,0,121,475,0]Ak[3974,13,3975,0,2436,475,0]Ak[3974,13,3975,0,2445,475,0]Aj[3974,13,3725,0,115,475,0]Ak[3974,13,3725,0,3134,475,0]Ak[3974,13,3725,0,1337,475,0]Ak[3974,13,3725,0,4014,475,0]Ak[3974,13,3725,0,2003,475,0]Ak[3974,13,3725,0,4307,475,0]Ak[3974,13,3725,0,4795,475,0]Ak[3974,13,1855,0,1998,475,0]Aj[3974,13,1855,0,476,475,0]Ak[3974,13,1855,0,5112,475,0]Ak[3974,13,1855,0,4012,475,0]Ak[3974,13,1855,0,4867,475,0]Ak[3974,13,1934,0,3365,475,0]Ak[3974,13,1934,0,4290,475,0]Ak[3974,13,1934,0,4733,475,0]Ak[3974,13,1934,0,4735,475,0]Ak[3974,13,1934,0,4960,475,0]Ak[3974,13,1934,0,1350,475,0]An[4761,13,4762,4550,4280,475,0]An[4761,13,4762,3190,3140,475,0]An[4761,13,4762,1083,1578,475,0]An[4761,13,4762,2164,2024,475,0]An[4761,13,4762,2164,3351,475,0]An[4761,13,4762,2164,3357,475,0]An[4761,13,4762,3731,3326,475,0]An[4761,13,4762,1079,3348,475,0]An[4761,13,4762,1853,4320,475,0]An[4761,13,4762,2459,2414,475,0]An[4761,13,4762,2459,4286,475,0]An[4761,13,4762,1918,5171,475,0]An[4761,13,4762,4328,5141,475,0]An[4761,13,4762,1563,4538,475,0]An[4761,13,4762,1563,4174,475,0]An[4761,13,4762,1563,4415,475,0]An[4761,13,4762,1971,1614,475,0]An[4761,13,4762,1971,4965,475,0]An[4761,13,4762,3181,1334,475,0]An[4761,13,4762,3190,4806,475,0]An[4761,13,4762,1537,1597,475,0]An[4761,13,4762,1537,4185,475,0]An[4761,13,4762,1537,3346,475,0]An[4761,13,4762,1537,3161,475,0]An[4761,13,4762,1537,4860,475,0]An[4761,13,4762,1537,3321,475,0]An[4761,13,4762,1553,2449,475,0]An[4761,13,4762,1553,3332,475,0]An[4761,13,4762,1553,3163,475,0]An[4761,13,4762,1553,4524,475,0]An[4761,13,4762,1083,3166,475,0]An[4761,13,4762,1083,4315,475,0]An[4761,13,4762,1088,2031,475,0]An[4761,13,4762,1088,3371,475,0]An[4761,13,4762,1088,4018,475,0]An[4761,13,4762,2164,4036,475,0]An[4761,13,4762,2164,4425,475,0]An[4761,13,4762,2164,4151,475,0]An[4761,13,4762,3731,4520,475,0]An[4761,13,4762,3731,3158,475,0]An[4761,13,4762,3731,4391,475,0]An[4761,13,4762,3731,5050,475,0]An[4761,13,4762,3731,1355,475,0]An[4761,13,4762,1079,4534,475,0]An[4761,13,4762,3308,4541,475,0]An[4761,13,4762,3308,4830,475,0]An[4761,13,4762,3308,4845,475,0]An[4761,13,4762,3308,4020,475,0]An[4761,13,4762,1853,4401,475,0]An[4761,13,4762,1853,4191,475,0]An[4761,13,4762,1853,5022,475,0]An[4761,13,4762,1853,4143,475,0]An[4761,13,4762,1853,4010,475,0]An[4761,13,4762,1527,3146,475,0]An[4761,13,4762,1527,4406,475,0]An[4761,13,4762,1527,3338,475,0]An[4761,13,4762,1527,3360,475,0]An[4761,13,4762,1527,4158,475,0]An[4761,13,4762,1527,4518,475,0]An[4761,13,4762,1527,4708,475,0]An[4761,13,4762,1527,2407,475,0]An[4761,13,4762,2459,4712,475,0]An[4761,13,4762,2459,2418,475,0]An[4761,13,4762,2459,4154,475,0]An[4761,13,4762,2459,4004,475,0]An[4761,13,4762,2459,3354,475,0]An[4761,13,4762,1918,4310,475,0]An[4761,13,4762,1918,4532,475,0]An[4761,13,4762,1918,5053,475,0]An[4761,13,4762,1918,2420,475,0]An[4761,13,4762,1918,4804,475,0]An[4761,13,4762,1945,4522,475,0]An[4761,13,4762,1945,4287,475,0]An[4761,13,4762,1945,4157,475,0]An[4761,13,4762,1945,4162,475,0]Ak[4761,13,4762,0,4429,475,0]Ak[4761,13,4762,0,4864,475,0]An[4761,13,4762,4328,3335,475,0]An[4761,13,4762,4328,5146,475,0]An[4761,13,4762,4895,4738,475,0]Am[4761,13,4762,4762,490,475,0]Am[4761,13,4762,4762,122,475,0]An[4761,13,4762,4762,2437,475,0]An[4761,13,4762,4762,2446,475,0]An[4761,13,4762,4762,3144,475,0]An[4761,13,1851,1851,4321,475,0]An[4761,13,5006,5006,4905,475,0]An[4761,13,5006,5006,1352,475,0]An[4761,13,1856,1856,1999,475,0]Am[4761,13,1856,1856,477,475,0]An[4761,13,1856,1856,5113,475,0]An[4761,13,1856,1856,4013,475,0]An[4761,13,1856,1856,4870,475,0]An[4761,13,1935,1935,3366,475,0]An[4761,13,1935,1935,4291,475,0]An[4761,13,1935,1935,4734,475,0]An[4761,13,1935,1935,4736,475,0]An[4761,13,1935,1935,4961,475,0]Ak[4761,13,1935,0,1351,475,0]Al[4761,13,3726,137,116,475,0]Am[4761,13,3726,137,3135,475,0]An[4761,13,3726,1522,1338,475,0]An[4761,13,3726,1522,4015,475,0]An[4761,13,3726,2507,2004,475,0]An[4761,13,3726,2507,4308,475,0]An[4761,13,3726,3726,4796,475,0]An[4761,13,2135,2135,1353,475,0]An[4761,13,2135,2135,1356,475,0]An[4465,13,4466,4466,3369,475,0]Am[4465,13,4466,397,4430,475,0]An[4465,13,4466,4466,2434,475,0]An[4465,13,4466,4466,2447,475,0]An[4465,13,4466,4466,4163,475,0]An[1058,13,1059,1059,3137,475,0]An[1058,13,1059,1059,4899,475,0]Am[1058,13,1845,1845,130,475,0]An[1058,13,1552,1552,1371,475,0]An[1058,13,1552,1552,4038,475,0]An[1058,13,1552,1552,4528,475,0]An[1058,13,4248,4248,5098,475,0]An[1058,13,4248,4248,5099,475,0]Am[1058,13,1940,1940,131,475,0]Am[3994,13,3995,3995,120,475,0]An[3994,13,3995,3995,2404,475,0]Ac[499,7,500,0,0,0,0]Ae[4043,7,4044,0,0,0,0]Ae[4331,7,4332,0,0,0,0]Ae[3994,7,3995,0,0,0,0]Ae[4824,7,4825,0,0,0,0]Al[4761,13,4762,1083,1578,0,0]Ai[3974,12,1855,0,1982,0,0]Al[4761,13,4762,2164,2024,0,0]Al[4761,12,1856,1856,1983,0,0]Al[3974,13,3975,3730,3325,0,0]Ab[1058,3,0,0,0,0,0]Ak[3974,13,3975,1057,482,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,13,4762,3731,3326,0,0]Al[4761,14,4762,1553,3332,0,0]Al[3974,12,3975,2156,3314,0,0]Ao[3974,13,3975,3730,4545,1586,0]Ao[3974,13,3975,3730,2427,1586,0]Ao[3974,13,3975,1057,2428,1586,0]Ao[3974,13,3975,3730,3323,1586,0]Ao[3974,13,3975,3730,2006,1586,0]Ao[3974,13,3975,3730,1589,1586,0]Ao[3974,13,3975,3730,3127,1586,0]Ao[3974,13,3975,3730,2401,1586,0]Ao[3974,13,3975,3730,4181,1586,0]Ao[3974,13,3975,3730,4186,1586,0]Ao[3974,13,3975,2458,1570,1586,0]Ao[3974,13,3975,2485,4405,1586,0]Ao[3974,13,3975,4115,2020,1586,0]?Al[3974,13,3975,4110,3350,0,0]Al[4761,13,4762,2164,3351,0,0]Al[4761,13,5006,5006,4905,0,0]Al[4761,13,4762,3190,3140,0,0]Ak[4465,13,4466,397,4430,0,0]Ai[3974,12,1855,0,1982,0,0]Al[4761,12,1856,1856,1983,0,0]32Ai[3974,13,3975,0,5021,0,0]Al[4761,13,4762,1853,5022,0,0]Al[3974,13,3975,3730,3325,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,12,1855,0,1994,0,0]Al[4761,12,1856,1856,1995,0,0]Al[1374,14,1551,1551,4426,0,0]Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Ai[4761,14,4531,0,1367,0,0]Al[3974,13,3975,4110,3350,0,0]Al[3974,13,3975,4110,4906,0,0]Al[4761,13,4762,2164,3351,0,0]Ai[3974,12,3975,0,3367,0,0]Al[3974,13,3975,0,4414,3367,0]Al[3974,13,3975,0,4737,3367,0]Al[4761,12,4762,1563,3368,0,0]Ao[4761,13,4762,1563,4415,3368,0]Ao[4761,13,4762,4895,4738,3368,0]Am[1374,13,1375,1375,129,492,0]An[1374,13,1375,1375,4006,492,0]An[1374,13,1551,1551,4037,492,0]An[1374,13,1551,1551,1370,492,0]An[1374,13,1551,1551,4426,492,0]Ak[499,13,500,500,4007,492,0]An[4043,13,4044,4044,5054,492,0]An[3974,13,3975,3730,4545,492,0]An[3974,13,3975,3730,2427,492,0]Ak[3974,13,3975,0,4279,492,0]An[3974,13,3975,1057,2428,492,0]An[3974,13,3975,1057,4704,492,0]An[3974,13,3975,3730,3323,492,0]An[3974,13,3975,3730,3325,492,0]An[3974,13,3975,3730,2006,492,0]An[3974,13,3975,3730,1589,492,0]An[3974,13,3975,3730,3127,492,0]An[3974,13,3975,3730,2401,492,0]An[3974,13,3975,3730,4181,492,0]An[3974,13,3975,3730,4186,492,0]An[3974,13,3975,3730,4967,492,0]An[3974,13,3975,3730,4805,492,0]An[3974,13,3975,2458,2413,492,0]An[3974,13,3975,2458,4288,492,0]An[3974,13,3975,2458,1570,492,0]An[3974,13,3975,2485,2417,492,0]An[3974,13,3975,2485,3353,492,0]An[3974,13,3975,2485,4405,492,0]Am[3974,13,3975,1057,482,492,0]An[3974,13,3975,1057,4431,492,0]An[3974,13,3975,4110,3350,492,0]An[3974,13,3975,4110,4906,492,0]An[3974,13,3975,4110,4424,492,0]An[3974,13,3975,4110,3165,492,0]An[3974,13,3975,4110,3352,492,0]An[3974,13,3975,4110,4423,492,0]An[3974,13,3975,1564,4196,492,0]An[3974,13,3975,1100,4032,492,0]An[3974,13,3975,4115,3356,492,0]An[3974,13,3975,4115,3370,492,0]An[3974,13,3975,4115,4195,492,0]An[3974,13,3975,4115,2020,492,0]Ak[3974,13,3975,0,3347,492,0]Ak[3974,13,3975,0,4319,492,0]Ak[3974,13,3975,0,4537,492,0]Ak[3974,13,3975,0,4173,492,0]Ak[3974,13,3975,0,4414,492,0]Ak[3974,13,3975,0,1613,492,0]Ak[3974,13,3975,0,4964,492,0]Ak[3974,13,3975,0,1333,492,0]Ak[3974,13,3975,0,1596,492,0]Ak[3974,13,3975,0,2448,492,0]Ak[3974,13,3975,0,3331,492,0]Ak[3974,13,3975,0,3162,492,0]Ak[3974,13,3975,0,4523,492,0]Ak[3974,13,3975,0,2030,492,0]Ak[3974,13,3975,0,4147,492,0]Ak[3974,13,3975,0,4519,492,0]Ak[3974,13,3975,0,4533,492,0]Ak[3974,13,3975,0,4540,492,0]Ak[3974,13,3975,0,4829,492,0]Ak[3974,13,3975,0,4844,492,0]Ak[3974,13,3975,0,4019,492,0]Ak[3974,13,3975,0,4400,492,0]Ak[3974,13,3975,0,4189,492,0]Ak[3974,13,3975,0,5021,492,0]Ak[3974,13,3975,0,4141,492,0]Ak[3974,13,3975,0,4009,492,0]Ak[3974,13,3975,0,3145,492,0]Ak[3974,13,3975,0,4516,492,0]Ak[3974,13,3975,0,4706,492,0]Ak[3974,13,3975,0,4153,492,0]Ak[3974,13,3975,0,4156,492,0]Ak[3974,13,3975,0,4161,492,0]Ak[3974,13,3975,0,4863,492,0]Ak[3974,13,3975,0,3334,492,0]Ak[3974,13,3975,0,4737,492,0]Aj[3974,13,3975,0,489,492,0]Aj[3974,13,3975,0,121,492,0]Ak[3974,13,3975,0,2436,492,0]Ak[3974,13,3975,0,2445,492,0]Aj[3974,13,3725,0,115,492,0]Ak[3974,13,3725,0,3134,492,0]Ak[3974,13,3725,0,1337,492,0]Ak[3974,13,3725,0,4014,492,0]Ak[3974,13,3725,0,2003,492,0]Ak[3974,13,3725,0,4307,492,0]Ak[3974,13,3725,0,4795,492,0]Ak[3974,13,1855,0,1998,492,0]Aj[3974,13,1855,0,476,492,0]Ak[3974,13,1855,0,5112,492,0]Ak[3974,13,1855,0,4012,492,0]Ak[3974,13,1855,0,4867,492,0]Ak[3974,13,1934,0,3365,492,0]Ak[3974,13,1934,0,4290,492,0]Ak[3974,13,1934,0,4733,492,0]Ak[3974,13,1934,0,4735,492,0]Ak[3974,13,1934,0,4960,492,0]Ak[3974,13,1934,0,1350,492,0]An[4761,13,4762,4550,4280,492,0]An[4761,13,4762,3190,3140,492,0]An[4761,13,4762,1083,1578,492,0]An[4761,13,4762,2164,2024,492,0]An[4761,13,4762,2164,3351,492,0]An[4761,13,4762,2164,3357,492,0]An[4761,13,4762,3731,3326,492,0]An[4761,13,4762,1079,3348,492,0]An[4761,13,4762,1853,4320,492,0]An[4761,13,4762,2459,2414,492,0]An[4761,13,4762,2459,4286,492,0]An[4761,13,4762,1918,5171,492,0]An[4761,13,4762,4328,5141,492,0]An[4761,13,4762,1563,4538,492,0]An[4761,13,4762,1563,4174,492,0]An[4761,13,4762,1563,4415,492,0]An[4761,13,4762,1971,1614,492,0]An[4761,13,4762,1971,4965,492,0]An[4761,13,4762,3181,1334,492,0]An[4761,13,4762,3190,4806,492,0]An[4761,13,4762,1537,1597,492,0]An[4761,13,4762,1537,4185,492,0]An[4761,13,4762,1537,3346,492,0]An[4761,13,4762,1537,3161,492,0]An[4761,13,4762,1537,4860,492,0]An[4761,13,4762,1537,3321,492,0]An[4761,13,4762,1553,2449,492,0]An[4761,13,4762,1553,3332,492,0]An[4761,13,4762,1553,3163,492,0]An[4761,13,4762,1553,4524,492,0]An[4761,13,4762,1083,3166,492,0]An[4761,13,4762,1083,4315,492,0]An[4761,13,4762,1088,2031,492,0]An[4761,13,4762,1088,3371,492,0]An[4761,13,4762,1088,4018,492,0]An[4761,13,4762,2164,4036,492,0]An[4761,13,4762,2164,4425,492,0]An[4761,13,4762,2164,4151,492,0]An[4761,13,4762,3731,4520,492,0]An[4761,13,4762,3731,3158,492,0]An[4761,13,4762,3731,4391,492,0]An[4761,13,4762,3731,5050,492,0]An[4761,13,4762,3731,1355,492,0]An[4761,13,4762,1079,4534,492,0]An[4761,13,4762,3308,4541,492,0]An[4761,13,4762,3308,4830,492,0]An[4761,13,4762,3308,4845,492,0]An[4761,13,4762,3308,4020,492,0]An[4761,13,4762,1853,4401,492,0]An[4761,13,4762,1853,4191,492,0]An[4761,13,4762,1853,5022,492,0]An[4761,13,4762,1853,4143,492,0]An[4761,13,4762,1853,4010,492,0]An[4761,13,4762,1527,3146,492,0]An[4761,13,4762,1527,4406,492,0]An[4761,13,4762,1527,3338,492,0]An[4761,13,4762,1527,3360,492,0]An[4761,13,4762,1527,4158,492,0]An[4761,13,4762,1527,4518,492,0]An[4761,13,4762,1527,4708,492,0]An[4761,13,4762,1527,2407,492,0]An[4761,13,4762,2459,4712,492,0]An[4761,13,4762,2459,2418,492,0]An[4761,13,4762,2459,4154,492,0]An[4761,13,4762,2459,4004,492,0]An[4761,13,4762,2459,3354,492,0]An[4761,13,4762,1918,4310,492,0]An[4761,13,4762,1918,4532,492,0]An[4761,13,4762,1918,5053,492,0]An[4761,13,4762,1918,2420,492,0]An[4761,13,4762,1918,4804,492,0]An[4761,13,4762,1945,4522,492,0]An[4761,13,4762,1945,4287,492,0]An[4761,13,4762,1945,4157,492,0]An[4761,13,4762,1945,4162,492,0]Ak[4761,13,4762,0,4429,492,0]Ak[4761,13,4762,0,4864,492,0]An[4761,13,4762,4328,3335,492,0]An[4761,13,4762,4328,5146,492,0]An[4761,13,4762,4895,4738,492,0]Am[4761,13,4762,4762,490,492,0]Am[4761,13,4762,4762,122,492,0]An[4761,13,4762,4762,2437,492,0]An[4761,13,4762,4762,2446,492,0]An[4761,13,4762,4762,3144,492,0]An[4761,13,1851,1851,4321,492,0]An[4761,13,5006,5006,4905,492,0]An[4761,13,5006,5006,1352,492,0]An[4761,13,1856,1856,1999,492,0]Am[4761,13,1856,1856,477,492,0]An[4761,13,1856,1856,5113,492,0]An[4761,13,1856,1856,4013,492,0]An[4761,13,1856,1856,4870,492,0]An[4761,13,1935,1935,3366,492,0]An[4761,13,1935,1935,4291,492,0]An[4761,13,1935,1935,4734,492,0]An[4761,13,1935,1935,4736,492,0]An[4761,13,1935,1935,4961,492,0]Ak[4761,13,1935,0,1351,492,0]Al[4761,13,3726,137,116,492,0]Am[4761,13,3726,137,3135,492,0]An[4761,13,3726,1522,1338,492,0]An[4761,13,3726,1522,4015,492,0]An[4761,13,3726,2507,2004,492,0]An[4761,13,3726,2507,4308,492,0]An[4761,13,3726,3726,4796,492,0]An[4761,13,2135,2135,1353,492,0]An[4761,13,2135,2135,1356,492,0]An[1058,13,1059,1059,3137,492,0]An[1058,13,1059,1059,4899,492,0]Am[1058,13,1845,1845,130,492,0]An[1058,13,1552,1552,1371,492,0]An[1058,13,1552,1552,4038,492,0]An[1058,13,1552,1552,4528,492,0]An[1058,13,4248,4248,5098,492,0]An[1058,13,4248,4248,5099,492,0]Am[1058,13,1940,1940,131,492,0]Am[3994,13,3995,3995,120,492,0]An[3994,13,3995,3995,2404,492,0]Ah[3974,5,3975,3181,0,0,0]Ah[4761,5,4762,3181,0,0,0]Ah[3974,5,3725,1522,0,0,0]Ah[4761,5,3726,1522,0,0,0]Al[1058,15,1059,1059,4899,0,0]Ah[3974,6,1934,1525,0,0,0]Ah[4761,6,1935,1525,0,0,0]Ae[4761,6,5006,0,0,0,0]Ae[4761,6,2135,0,0,0,0]Ah[4761,6,4762,3731,0,0,0]1Al[4761,15,4762,1527,3338,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Al[3974,15,3975,4110,3165,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,1083,3166,0,0]Al[4761,15,4762,3731,4520,0,0]Al[4761,15,4762,1918,4532,0,0]0Ae[1374,5,1551,0,0,0,0]Ae[1058,5,1552,0,0,0,0]Ab[1374,3,0,0,0,0,0]Ai[3974,12,3725,0,4793,0,0]Al[3974,13,3725,0,3134,4793,0]Al[3974,13,3725,0,4014,4793,0]Al[3974,13,3725,0,4307,4793,0]Al[4761,12,3726,3726,4794,0,0]An[4761,13,3726,137,3135,4794,0]Ao[4761,13,3726,1522,4015,4794,0]Ao[4761,13,3726,2507,4308,4794,0]Al[1058,13,1059,1059,4899,0,0]Ai[3974,14,3975,0,1333,0,0]Ai[3974,14,3975,0,2448,0,0]Ai[3974,14,3975,0,2030,0,0]Al[4761,14,4762,3181,1334,0,0]Al[4761,14,4762,1553,2449,0,0]Al[4761,14,4762,1088,2031,0,0]Al[4761,14,4762,2459,4004,0,0]An[1374,13,1375,1375,129,1335,0]Ao[1374,13,1375,1375,4006,1335,0]Ao[3974,13,3975,3730,3325,1335,0]Ao[3974,13,3975,3730,4967,1335,0]Ao[3974,13,3975,3730,4805,1335,0]Ao[3974,13,3975,2458,2413,1335,0]Ao[3974,13,3975,2458,4288,1335,0]An[3974,13,3975,1057,482,1335,0]Ao[3974,13,3975,1057,4431,1335,0]Ao[3974,13,3975,4110,4424,1335,0]Ao[3974,13,3975,4110,3165,1335,0]Ao[3974,13,3975,4110,3352,1335,0]Ao[3974,13,3975,4110,4423,1335,0]Ao[3974,13,3975,4115,3370,1335,0]Ao[3974,13,3975,4115,4195,1335,0]Al[3974,13,3975,0,1613,1335,0]Al[3974,13,3975,0,4964,1335,0]Al[3974,13,3975,0,1596,1335,0]Al[3974,13,3975,0,2448,1335,0]Al[3974,13,3975,0,3331,1335,0]Al[3974,13,3975,0,3162,1335,0]Al[3974,13,3975,0,4523,1335,0]Al[3974,13,3975,0,2030,1335,0]Al[3974,13,3975,0,4147,1335,0]Al[3974,13,3975,0,4519,1335,0]Al[3974,13,3975,0,4540,1335,0]Al[3974,13,3975,0,4829,1335,0]Al[3974,13,3975,0,4844,1335,0]Al[3974,13,3975,0,4019,1335,0]Al[3974,13,3975,0,4400,1335,0]Al[3974,13,3975,0,4189,1335,0]Al[3974,13,3975,0,5021,1335,0]Al[3974,13,3975,0,4141,1335,0]Al[3974,13,3975,0,4009,1335,0]Al[3974,13,3975,0,3145,1335,0]Al[3974,13,3975,0,4516,1335,0]Al[3974,13,3975,0,4706,1335,0]Al[3974,13,3975,0,4153,1335,0]Al[3974,13,3975,0,4156,1335,0]Al[3974,13,3975,0,4161,1335,0]Al[3974,13,3975,0,4863,1335,0]Al[3974,13,3975,0,3334,1335,0]Ak[3974,13,3975,0,489,1335,0]Ak[3974,13,3975,0,121,1335,0]Al[3974,13,3975,0,2436,1335,0]Ak[3974,13,3725,0,115,1335,0]Al[3974,13,3725,0,3134,1335,0]Al[3974,13,3725,0,1337,1335,0]Al[3974,13,3725,0,4014,1335,0]Al[3974,13,3725,0,2003,1335,0]Al[3974,13,3725,0,4307,1335,0]Al[3974,13,1934,0,4960,1335,0]Al[3974,13,1934,0,1350,1335,0]Ao[4761,13,4762,1971,1614,1335,0]Ao[4761,13,4762,1971,4965,1335,0]Ao[4761,13,4762,3190,4806,1335,0]Ao[4761,13,4762,1537,1597,1335,0]Ao[4761,13,4762,1537,4185,1335,0]Ao[4761,13,4762,1537,3346,1335,0]Ao[4761,13,4762,1537,3161,1335,0]Ao[4761,13,4762,1537,4860,1335,0]Ao[4761,13,4762,1537,3321,1335,0]Ao[4761,13,4762,1553,2449,1335,0]Ao[4761,13,4762,1553,3332,1335,0]Ao[4761,13,4762,1553,3163,1335,0]Ao[4761,13,4762,1553,4524,1335,0]Ao[4761,13,4762,1083,3166,1335,0]Ao[4761,13,4762,1083,4315,1335,0]Ao[4761,13,4762,1088,2031,1335,0]Ao[4761,13,4762,1088,3371,1335,0]Ao[4761,13,4762,1088,4018,1335,0]Ao[4761,13,4762,2164,4425,1335,0]Ao[4761,13,4762,2164,4151,1335,0]Ao[4761,13,4762,3731,4520,1335,0]Ao[4761,13,4762,3731,3158,1335,0]Ao[4761,13,4762,3731,4391,1335,0]Ao[4761,13,4762,3731,5050,1335,0]Ao[4761,13,4762,3308,4541,1335,0]Ao[4761,13,4762,3308,4830,1335,0]Ao[4761,13,4762,3308,4845,1335,0]Ao[4761,13,4762,3308,4020,1335,0]Ao[4761,13,4762,1853,4401,1335,0]Ao[4761,13,4762,1853,4191,1335,0]Ao[4761,13,4762,1853,5022,1335,0]Ao[4761,13,4762,1853,4143,1335,0]Ao[4761,13,4762,1853,4010,1335,0]Ao[4761,13,4762,1527,3146,1335,0]Ao[4761,13,4762,1527,3338,1335,0]Ao[4761,13,4762,1527,3360,1335,0]Ao[4761,13,4762,1527,4158,1335,0]Ao[4761,13,4762,1527,4518,1335,0]Ao[4761,13,4762,1527,4708,1335,0]Ao[4761,13,4762,1527,2407,1335,0]Ao[4761,13,4762,2459,4712,1335,0]Ao[4761,13,4762,2459,4154,1335,0]Ao[4761,13,4762,1918,4532,1335,0]Ao[4761,13,4762,1918,5053,1335,0]Ao[4761,13,4762,1918,2420,1335,0]Ao[4761,13,4762,1918,4804,1335,0]Ao[4761,13,4762,1945,4157,1335,0]Ao[4761,13,4762,1945,4162,1335,0]Al[4761,13,4762,0,4429,1335,0]Al[4761,13,4762,0,4864,1335,0]Ao[4761,13,4762,4328,3335,1335,0]Ao[4761,13,4762,4328,5146,1335,0]An[4761,13,4762,4762,490,1335,0]An[4761,13,4762,4762,122,1335,0]Ao[4761,13,4762,4762,2437,1335,0]Ao[4761,13,4762,4762,3144,1335,0]Ao[4761,13,1851,1851,4321,1335,0]Ao[4761,13,5006,5006,4905,1335,0]Ao[4761,13,5006,5006,1352,1335,0]Ao[4761,13,1935,1935,4961,1335,0]Al[4761,13,1935,0,1351,1335,0]Am[4761,13,3726,137,116,1335,0]An[4761,13,3726,137,3135,1335,0]Ao[4761,13,3726,1522,1338,1335,0]Ao[4761,13,3726,1522,4015,1335,0]Ao[4761,13,3726,2507,2004,1335,0]Ao[4761,13,3726,2507,4308,1335,0]Ao[4761,13,2135,2135,1356,1335,0]Ao[4465,13,4466,4466,2434,1335,0]Ao[4465,13,4466,4466,2447,1335,0]Ao[4465,13,4466,4466,4163,1335,0]Ao[1058,13,1059,1059,4899,1335,0]Ao[1058,13,1552,1552,4528,1335,0]Al[3974,13,3975,3730,3325,0,0]Al[3974,13,3975,2458,2413,0,0]Ak[3974,13,3975,1057,482,0,0]Al[4761,13,4762,3731,3326,0,0]Al[3974,13,3975,0,4156,1341,0]Ao[4761,13,4762,1945,4157,1341,0]Ai[4761,14,3159,0,4298,0,0]Ai[4761,14,4762,0,4429,0,0]Ai[3974,13,3975,0,2436,0,0]Al[4761,13,4762,4762,2437,0,0]Ai[3974,13,3975,0,4161,0,0]Al[4761,13,4762,1945,4162,0,0]Ai[3974,14,1855,0,1998,0,0]Al[4761,14,1856,1856,1999,0,0]Al[4761,12,4762,1918,4835,0,0]Ao[4761,13,4762,1918,5171,4835,0]Al[4761,13,5006,5006,4905,0,0]Ah[3974,5,3975,2458,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Al[1374,15,1375,1375,4006,0,0]0Al[4761,15,4762,1537,3346,0,0]Ah[4761,5,4762,1083,0,0,0]Ah[3974,15,3975,0,489,0,0]Ak[4761,15,4762,4762,490,0,0]Al[3994,15,3995,3995,2404,0,0]Al[4761,15,2135,2135,1353,0,0]87Ah[3974,5,3975,3730,0,0,0]Al[4761,15,4762,1537,3161,0,0]Al[4761,15,4762,2164,4036,0,0]Ah[3974,5,3975,1537,0,0,0]Ah[4761,5,4762,1537,0,0,0]Ae[4761,8,1851,0,0,0,0]987>Al[4761,15,4762,3731,1355,0,0]>Ah[3974,5,3975,1971,0,0,0]Ah[4761,5,4762,1971,0,0,0]Al[3974,13,3975,2458,2413,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2164,2024,0,0]Al[4761,13,4762,2459,2414,0,0]An[1374,13,1375,1375,129,1576,0]Ao[1374,13,1375,1375,4006,1576,0]Ao[1374,13,1551,1551,4037,1576,0]Ao[1374,13,1551,1551,1370,1576,0]Ao[1374,13,1551,1551,4426,1576,0]Al[499,13,500,500,4007,1576,0]Ao[4043,13,4044,4044,5054,1576,0]Ao[3974,13,3975,3730,4545,1576,0]Ao[3974,13,3975,3730,2427,1576,0]Al[3974,13,3975,0,4279,1576,0]Ao[3974,13,3975,1057,2428,1576,0]Ao[3974,13,3975,1057,4704,1576,0]Ao[3974,13,3975,3730,3323,1576,0]Ao[3974,13,3975,3730,3325,1576,0]Ao[3974,13,3975,3730,2006,1576,0]Ao[3974,13,3975,3730,1589,1576,0]Ao[3974,13,3975,3730,3127,1576,0]Ao[3974,13,3975,3730,2401,1576,0]Ao[3974,13,3975,3730,4181,1576,0]Ao[3974,13,3975,3730,4186,1576,0]Ao[3974,13,3975,3730,4967,1576,0]Ao[3974,13,3975,3730,4805,1576,0]Ao[3974,13,3975,2458,2413,1576,0]Ao[3974,13,3975,2458,4288,1576,0]Ao[3974,13,3975,2458,1570,1576,0]Ao[3974,13,3975,2485,2417,1576,0]Ao[3974,13,3975,2485,3353,1576,0]Ao[3974,13,3975,2485,4405,1576,0]An[3974,13,3975,1057,482,1576,0]Ao[3974,13,3975,1057,4431,1576,0]Ao[3974,13,3975,4110,3350,1576,0]Ao[3974,13,3975,4110,4906,1576,0]Ao[3974,13,3975,4110,4424,1576,0]Ao[3974,13,3975,4110,3165,1576,0]Ao[3974,13,3975,4110,3352,1576,0]Ao[3974,13,3975,4110,4423,1576,0]Ao[3974,13,3975,1564,4196,1576,0]Ao[3974,13,3975,1100,4032,1576,0]Ao[3974,13,3975,4115,3356,1576,0]Ao[3974,13,3975,4115,3370,1576,0]Ao[3974,13,3975,4115,4195,1576,0]Ao[3974,13,3975,4115,2020,1576,0]Al[3974,13,3975,0,3347,1576,0]Al[3974,13,3975,0,4319,1576,0]Al[3974,13,3975,0,4537,1576,0]Al[3974,13,3975,0,4173,1576,0]Al[3974,13,3975,0,4414,1576,0]Al[3974,13,3975,0,1613,1576,0]Al[3974,13,3975,0,4964,1576,0]Al[3974,13,3975,0,1333,1576,0]Al[3974,13,3975,0,1596,1576,0]Al[3974,13,3975,0,2448,1576,0]Al[3974,13,3975,0,3331,1576,0]Al[3974,13,3975,0,3162,1576,0]Al[3974,13,3975,0,4523,1576,0]Al[3974,13,3975,0,2030,1576,0]Al[3974,13,3975,0,4147,1576,0]Al[3974,13,3975,0,4519,1576,0]Al[3974,13,3975,0,4533,1576,0]Al[3974,13,3975,0,4540,1576,0]Al[3974,13,3975,0,4829,1576,0]Al[3974,13,3975,0,4844,1576,0]Al[3974,13,3975,0,4019,1576,0]Al[3974,13,3975,0,4400,1576,0]Al[3974,13,3975,0,4189,1576,0]Al[3974,13,3975,0,5021,1576,0]Al[3974,13,3975,0,4141,1576,0]Al[3974,13,3975,0,4009,1576,0]Al[3974,13,3975,0,3145,1576,0]Al[3974,13,3975,0,4516,1576,0]Al[3974,13,3975,0,4706,1576,0]Al[3974,13,3975,0,4153,1576,0]Al[3974,13,3975,0,4156,1576,0]Al[3974,13,3975,0,4161,1576,0]Al[3974,13,3975,0,4863,1576,0]Al[3974,13,3975,0,3334,1576,0]Al[3974,13,3975,0,4737,1576,0]Ak[3974,13,3975,0,489,1576,0]Ak[3974,13,3975,0,121,1576,0]Al[3974,13,3975,0,2436,1576,0]Al[3974,13,3975,0,2445,1576,0]Ak[3974,13,3725,0,115,1576,0]Al[3974,13,3725,0,3134,1576,0]Al[3974,13,3725,0,1337,1576,0]Al[3974,13,3725,0,4014,1576,0]Al[3974,13,3725,0,2003,1576,0]Al[3974,13,3725,0,4307,1576,0]Al[3974,13,3725,0,4795,1576,0]Al[3974,13,1855,0,1998,1576,0]Ak[3974,13,1855,0,476,1576,0]Al[3974,13,1855,0,5112,1576,0]Al[3974,13,1855,0,4012,1576,0]Al[3974,13,1855,0,4867,1576,0]Al[3974,13,1934,0,3365,1576,0]Al[3974,13,1934,0,4290,1576,0]Al[3974,13,1934,0,4733,1576,0]Al[3974,13,1934,0,4735,1576,0]Al[3974,13,1934,0,4960,1576,0]Al[3974,13,1934,0,1350,1576,0]Ao[4761,13,4762,4550,4280,1576,0]Ao[4761,13,4762,3190,3140,1576,0]Ao[4761,13,4762,1083,1578,1576,0]Ao[4761,13,4762,2164,2024,1576,0]Ao[4761,13,4762,2164,3351,1576,0]Ao[4761,13,4762,2164,3357,1576,0]Ao[4761,13,4762,3731,3326,1576,0]Ao[4761,13,4762,1079,3348,1576,0]Ao[4761,13,4762,1853,4320,1576,0]Ao[4761,13,4762,2459,2414,1576,0]Ao[4761,13,4762,2459,4286,1576,0]Ao[4761,13,4762,1918,5171,1576,0]Ao[4761,13,4762,4328,5141,1576,0]Ao[4761,13,4762,1563,4538,1576,0]Ao[4761,13,4762,1563,4174,1576,0]Ao[4761,13,4762,1563,4415,1576,0]Ao[4761,13,4762,1971,1614,1576,0]Ao[4761,13,4762,1971,4965,1576,0]Ao[4761,13,4762,3181,1334,1576,0]Ao[4761,13,4762,3190,4806,1576,0]Ao[4761,13,4762,1537,1597,1576,0]Ao[4761,13,4762,1537,4185,1576,0]Ao[4761,13,4762,1537,3346,1576,0]Ao[4761,13,4762,1537,3161,1576,0]Ao[4761,13,4762,1537,4860,1576,0]Ao[4761,13,4762,1537,3321,1576,0]Ao[4761,13,4762,1553,2449,1576,0]Ao[4761,13,4762,1553,3332,1576,0]Ao[4761,13,4762,1553,3163,1576,0]Ao[4761,13,4762,1553,4524,1576,0]Ao[4761,13,4762,1083,3166,1576,0]Ao[4761,13,4762,1083,4315,1576,0]Ao[4761,13,4762,1088,2031,1576,0]Ao[4761,13,4762,1088,3371,1576,0]Ao[4761,13,4762,1088,4018,1576,0]Ao[4761,13,4762,2164,4036,1576,0]Ao[4761,13,4762,2164,4425,1576,0]Ao[4761,13,4762,2164,4151,1576,0]Ao[4761,13,4762,3731,4520,1576,0]Ao[4761,13,4762,3731,3158,1576,0]Ao[4761,13,4762,3731,4391,1576,0]Ao[4761,13,4762,3731,5050,1576,0]Ao[4761,13,4762,3731,1355,1576,0]Ao[4761,13,4762,1079,4534,1576,0]Ao[4761,13,4762,3308,4541,1576,0]Ao[4761,13,4762,3308,4830,1576,0]Ao[4761,13,4762,3308,4845,1576,0]Ao[4761,13,4762,3308,4020,1576,0]Ao[4761,13,4762,1853,4401,1576,0]Ao[4761,13,4762,1853,4191,1576,0]Ao[4761,13,4762,1853,5022,1576,0]Ao[4761,13,4762,1853,4143,1576,0]Ao[4761,13,4762,1853,4010,1576,0]Ao[4761,13,4762,1527,3146,1576,0]Ao[4761,13,4762,1527,4406,1576,0]Ao[4761,13,4762,1527,3338,1576,0]Ao[4761,13,4762,1527,3360,1576,0]Ao[4761,13,4762,1527,4158,1576,0]Ao[4761,13,4762,1527,4518,1576,0]Ao[4761,13,4762,1527,4708,1576,0]Ao[4761,13,4762,1527,2407,1576,0]Ao[4761,13,4762,2459,4712,1576,0]Ao[4761,13,4762,2459,2418,1576,0]Ao[4761,13,4762,2459,4154,1576,0]Ao[4761,13,4762,2459,4004,1576,0]Ao[4761,13,4762,2459,3354,1576,0]Ao[4761,13,4762,1918,4310,1576,0]Ao[4761,13,4762,1918,4532,1576,0]Ao[4761,13,4762,1918,5053,1576,0]Ao[4761,13,4762,1918,2420,1576,0]Ao[4761,13,4762,1918,4804,1576,0]Ao[4761,13,4762,1945,4522,1576,0]Ao[4761,13,4762,1945,4287,1576,0]Ao[4761,13,4762,1945,4157,1576,0]Ao[4761,13,4762,1945,4162,1576,0]Al[4761,13,4762,0,4429,1576,0]Al[4761,13,4762,0,4864,1576,0]Ao[4761,13,4762,4328,3335,1576,0]Ao[4761,13,4762,4328,5146,1576,0]Ao[4761,13,4762,4895,4738,1576,0]An[4761,13,4762,4762,490,1576,0]An[4761,13,4762,4762,122,1576,0]Ao[4761,13,4762,4762,2437,1576,0]Ao[4761,13,4762,4762,2446,1576,0]Ao[4761,13,4762,4762,3144,1576,0]Ao[4761,13,1851,1851,4321,1576,0]Ao[4761,13,5006,5006,4905,1576,0]Ao[4761,13,5006,5006,1352,1576,0]Ao[4761,13,1856,1856,1999,1576,0]An[4761,13,1856,1856,477,1576,0]Ao[4761,13,1856,1856,5113,1576,0]Ao[4761,13,1856,1856,4013,1576,0]Ao[4761,13,1856,1856,4870,1576,0]Ao[4761,13,1935,1935,3366,1576,0]Ao[4761,13,1935,1935,4291,1576,0]Ao[4761,13,1935,1935,4734,1576,0]Ao[4761,13,1935,1935,4736,1576,0]Ao[4761,13,1935,1935,4961,1576,0]Al[4761,13,1935,0,1351,1576,0]Am[4761,13,3726,137,116,1576,0]An[4761,13,3726,137,3135,1576,0]Ao[4761,13,3726,1522,1338,1576,0]Ao[4761,13,3726,1522,4015,1576,0]Ao[4761,13,3726,2507,2004,1576,0]Ao[4761,13,3726,2507,4308,1576,0]Ao[4761,13,3726,3726,4796,1576,0]Ao[4761,13,2135,2135,1353,1576,0]Ao[4761,13,2135,2135,1356,1576,0]Ao[4465,13,4466,4466,3369,1576,0]An[4465,13,4466,397,4430,1576,0]Ao[4465,13,4466,4466,2434,1576,0]Ao[4465,13,4466,4466,2447,1576,0]Ao[4465,13,4466,4466,4163,1576,0]Ao[1058,13,1059,1059,3137,1576,0]Ao[1058,13,1059,1059,4899,1576,0]An[1058,13,1845,1845,130,1576,0]Ao[1058,13,1552,1552,1371,1576,0]Ao[1058,13,1552,1552,4038,1576,0]Ao[1058,13,1552,1552,4528,1576,0]Ao[1058,13,4248,4248,5098,1576,0]Ao[1058,13,4248,4248,5099,1576,0]An[1058,13,1940,1940,131,1576,0]An[3994,13,3995,3995,120,1576,0]Ao[3994,13,3995,3995,2404,1576,0]Ai[3974,14,3975,0,2030,0,0]Al[4761,14,4762,1088,2031,0,0]Ak[1374,14,1375,1375,129,0,0]Ac[499,7,500,0,0,0,0]Al[3974,13,3975,2458,2413,0,0]Ae[1058,2,1059,0,0,0,0]Ae[4824,7,4825,0,0,0,0]Al[3974,13,3975,3730,3325,0,0]Ae[4761,2,4762,0,0,0,0]Al[4761,13,4762,2459,2414,0,0]Ah[3974,2,3975,4762,0,0,0]Ai[3974,13,3975,0,4863,0,0]Ai[3974,14,3975,0,3331,0,0]Ai[3974,14,3975,0,4189,0,0]5Ai[4761,13,4762,0,4864,0,0]Al[4761,14,4762,1553,3332,0,0]Al[4761,14,4762,1853,4191,0,0]Ai[4761,14,4762,0,4429,0,0]Al[3974,13,3975,0,1613,3141,0]Al[3974,13,3975,0,1596,3141,0]Al[3974,13,3975,0,3145,3141,0]Al[3974,13,3975,0,4863,3141,0]Al[3974,13,3975,0,2436,3141,0]Ao[4761,13,4762,1971,1614,3141,0]Ao[4761,13,4762,1537,1597,3141,0]Ao[4761,13,4762,1527,3146,3141,0]Ao[4761,13,4762,1527,3338,3141,0]Al[4761,13,4762,1918,4532,0,0]Al[4761,13,4762,0,4864,3141,0]Ao[4761,13,4762,4762,2437,3141,0]Al[4761,12,1851,1851,3141,0,0]Al[4465,14,4466,4466,2434,0,0]Al[3974,13,3975,0,1613,3142,0]Al[3974,13,3975,0,1596,3142,0]Al[3974,13,3975,0,3145,3142,0]Al[3974,13,3975,0,4863,3142,0]Al[3974,13,3975,0,2436,3142,0]Ao[4761,13,4762,1971,1614,3142,0]Ao[4761,13,4762,1537,1597,3142,0]Ao[4761,13,4762,1527,3146,3142,0]Ao[4761,13,4762,1527,3338,3142,0]Ao[4761,13,4762,1527,2407,3142,0]Al[4761,13,4762,0,4864,3142,0]Ao[4761,13,4762,4762,2437,3142,0]Al[4761,12,1851,1851,3142,0,0]Al[3974,13,3975,4115,3356,0,0]Al[4761,13,4762,2164,3357,0,0]Al[1058,13,1552,1552,4038,0,0]Al[4761,13,4762,2459,2414,0,0]Ah[3974,14,1855,0,476,0,0]Ak[4761,14,1856,1856,477,0,0]Al[3974,13,3975,2485,2417,0,0]3Ai[3974,12,3975,0,3367,0,0]Al[3974,13,3975,0,4414,3367,0]Al[3974,13,3975,0,4737,3367,0]Al[4761,12,4762,1563,3368,0,0]Ao[4761,13,4762,1563,4415,3368,0]Ao[4761,13,4762,4895,4738,3368,0]Al[1058,14,1059,1059,3137,0,0]Ai[3974,14,3975,0,1333,0,0]Al[4761,14,4762,3181,1334,0,0]Ai[3974,13,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,13,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Ah[3974,14,1855,0,476,0,0]Ak[4761,14,1856,1856,477,0,0]Ai[4761,14,4531,0,4741,0,0]Ai[3974,14,3975,0,4516,0,0]Ai[3974,14,3975,0,4706,0,0]Al[4761,14,4762,1527,4518,0,0]Al[4761,14,4762,1527,4708,0,0]Ai[4761,14,4762,0,4429,0,0]Ai[4761,14,1354,0,4393,0,0]Al[3974,13,3975,2458,2413,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2459,2414,0,0]Ai[3974,14,1855,0,1998,0,0]Al[4761,14,4762,4762,3144,0,0]Al[4761,14,1856,1856,1999,0,0]Ae[3974,4,3975,0,0,0,0]Ah[3974,2,1855,1856,0,0,0]Ae[4761,4,4762,0,0,0,0]Ae[4761,2,1856,0,0,0,0]Ac[499,7,500,0,0,0,0]:Ae[1058,2,1059,0,0,0,0]Ae[4824,7,4825,0,0,0,0]Ao[3974,13,3975,3730,4805,1343,0]Ao[3974,13,3975,4110,4424,1343,0]Ao[3974,13,3975,4110,3165,1343,0]Ao[3974,13,3975,4110,3352,1343,0]Ao[3974,13,3975,4110,4423,1343,0]Ao[3974,13,3975,4115,3370,1343,0]Ao[3974,13,3975,4115,4195,1343,0]Al[3974,13,3975,0,4519,1343,0]Ao[4761,13,4762,3731,4520,1343,0]Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Al[4465,14,4466,4466,2447,0,0]Ak[3974,13,3975,1057,482,0,0]Ai[4761,14,3159,0,4182,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]Al[4465,14,4466,4466,2434,0,0]Ai[3974,14,3975,0,3162,0,0]Ai[3974,14,3975,0,4523,0,0]Al[4761,14,4762,1553,3163,0,0]Al[4761,14,4762,1553,4524,0,0]3210Ai[3974,12,1855,0,1994,0,0]Al[4761,12,1856,1856,1995,0,0]Ai[3974,12,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,12,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Ai[3974,10,1855,1856,0,0,0]Af[4761,10,1856,0,0,0,0]Al[4761,15,4762,3190,4806,0,0]Al[4761,15,4762,1083,3166,0,0]Al[4761,15,4762,1088,3371,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]65Al[4761,15,4762,1918,4532,0,0]Ah[3974,5,1855,1856,0,0,0]Ae[4761,5,1856,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[3974,5,3725,2507,0,0,0]Ah[4761,5,3726,2507,0,0,0]Al[1058,15,1059,1059,4899,0,0]Ah[3974,5,3975,3730,0,0,0]5454Ai[3974,10,3975,2156,0,0,0]Ai[3974,10,1855,1856,0,0,0]Af[4761,10,1856,0,0,0,0]Al[3974,15,3975,4115,3370,0,0]Ah[3974,5,3975,4115,0,0,0]Al[4761,15,4762,1088,3371,0,0]Al[4761,15,4762,1088,4018,0,0]Al[4761,15,4762,2164,4036,0,0]Ah[4761,5,4762,2164,0,0,0]Al[3974,15,3975,4110,4424,0,0]Al[4761,15,4762,1527,3338,0,0]Ah[3974,5,3975,1088,0,0,0]Ah[4761,5,4762,1088,0,0,0]Al[4761,15,4762,1918,2420,0,0]Ai[3974,12,3975,0,5012,0,0]Al[4761,12,4762,4328,5013,0,0]Ao[4761,13,4762,4328,5141,5013,0]Ao[4761,13,4762,4328,5146,5013,0]Ak[1374,14,1375,1375,129,0,0]An[1374,13,1375,1375,129,4392,0]Ao[1058,13,1059,1059,3137,4392,0]An[1058,13,1845,1845,130,4392,0]An[1058,13,1940,1940,131,4392,0]An[3994,13,3995,3995,120,4392,0]Ak[3994,14,3995,3995,120,0,0]Al[3974,13,3975,0,1613,3322,0]Al[3974,13,3975,0,1596,3322,0]Al[3974,13,3975,0,4019,3322,0]Al[3974,13,3975,0,4156,3322,0]Ak[3974,13,3975,0,489,3322,0]Ak[3974,13,3975,0,121,3322,0]Al[3974,13,3975,0,2436,3322,0]Ao[4761,13,4762,1971,1614,3322,0]Ao[4761,13,4762,3190,4806,3322,0]Ao[4761,13,4762,1537,1597,3322,0]Ao[4761,13,4762,1083,3166,3322,0]Ao[4761,13,4762,1083,4315,3322,0]Ao[4761,13,4762,1088,3371,3322,0]Ao[4761,13,4762,3308,4020,3322,0]Ao[4761,13,4762,2459,4712,3322,0]Ao[4761,13,4762,1918,4804,3322,0]Ao[4761,13,4762,1945,4157,3322,0]An[4761,13,4762,4762,490,3322,0]An[4761,13,4762,4762,122,3322,0]Ao[4761,13,4762,4762,2437,3322,0]Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,2459,2414,0,0]Ai[3974,12,1855,0,3155,0,0]Ai[3974,12,1855,0,3148,0,0]Al[4761,12,1856,1856,3156,0,0]Al[4761,12,1856,1856,3149,0,0]Ao[1374,13,1551,1551,4037,1989,0]Ao[1374,13,1551,1551,1370,1989,0]Al[499,13,500,500,4007,1989,0]Al[3974,13,3975,0,4279,1989,0]Ao[3974,13,3975,2458,4288,1989,0]Al[3974,13,3975,0,1613,1989,0]Al[3974,13,3975,0,1596,1989,0]Al[3974,13,3975,0,2448,1989,0]Al[3974,13,3975,0,3331,1989,0]Al[3974,13,3975,0,3162,1989,0]Al[3974,13,3975,0,4523,1989,0]Al[3974,13,3975,0,2030,1989,0]Al[3974,13,3975,0,4147,1989,0]Al[3974,13,3975,0,4540,1989,0]Al[3974,13,3975,0,4829,1989,0]Al[3974,13,3975,0,4844,1989,0]Al[3974,13,3975,0,4019,1989,0]Al[3974,13,3975,0,4400,1989,0]Al[3974,13,3975,0,5021,1989,0]Al[3974,13,3975,0,4737,1989,0]Al[3974,13,3725,0,3134,1989,0]Al[3974,13,3725,0,4014,1989,0]Al[3974,13,3725,0,4307,1989,0]Al[3974,13,3725,0,4795,1989,0]Ao[4761,13,4762,4550,4280,1989,0]Ao[4761,13,4762,4328,5141,1989,0]Ao[4761,13,4762,1971,1614,1989,0]Ao[4761,13,4762,1537,1597,1989,0]Ao[4761,13,4762,1537,3346,1989,0]Ao[4761,13,4762,1537,3321,1989,0]Ao[4761,13,4762,1553,2449,1989,0]Ao[4761,13,4762,1553,3332,1989,0]Ao[4761,13,4762,1553,3163,1989,0]Ao[4761,13,4762,1553,4524,1989,0]Ao[4761,13,4762,1088,2031,1989,0]Ao[4761,13,4762,2164,4425,1989,0]Ao[4761,13,4762,2164,4151,1989,0]Ao[4761,13,4762,3308,4541,1989,0]Ao[4761,13,4762,3308,4830,1989,0]Ao[4761,13,4762,3308,4845,1989,0]Ao[4761,13,4762,3308,4020,1989,0]Ao[4761,13,4762,1853,4401,1989,0]Ao[4761,13,4762,1853,5022,1989,0]Ao[4761,13,4762,4328,5146,1989,0]Ao[4761,13,4762,4895,4738,1989,0]An[4761,13,3726,137,3135,1989,0]Ao[4761,13,3726,1522,4015,1989,0]Ao[4761,13,3726,2507,4308,1989,0]Ao[4761,13,3726,3726,4796,1989,0]Ao[4465,13,4466,4466,2434,1989,0]Ao[1058,13,1552,1552,1371,1989,0]Ao[1058,13,1552,1552,4528,1989,0]Al[4761,13,4762,1945,4287,0,0]Al[4761,13,4762,1918,4310,0,0]Al[4761,13,4762,1918,4532,0,0]Al[4761,14,4762,1918,5053,0,0]Ai[4761,14,4803,0,4865,0,0]Ah[4761,14,4531,0,478,0,0]Ai[4761,14,4531,0,1365,0,0]Ai[4761,14,4531,0,1367,0,0]Ai[4761,14,4531,0,4741,0,0]Ai[3974,12,1855,0,3155,0,0]Ai[3974,12,1855,0,3148,0,0]Al[4761,12,1856,1856,3156,0,0]Al[4761,12,1856,1856,3149,0,0]Ae[4761,2,4762,0,0,0,0]Al[3974,13,3975,4110,3350,0,0]Al[3974,13,3975,4110,4906,0,0]Ai[3974,13,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,13,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]9854Al[3974,13,3975,3730,3325,0,0]Al[3974,13,3975,1057,2428,0,0]Al[4761,14,4762,1918,5053,0,0]Ai[4761,14,4803,0,4865,0,0]Ah[4761,14,4531,0,478,0,0]Ai[4761,14,4531,0,1365,0,0]Ai[4761,14,4531,0,4741,0,0]Al[3974,13,3975,4115,3356,0,0]Al[4761,13,4762,2164,3357,0,0]Ak[1374,14,1375,1375,129,0,0]Al[1374,14,1551,1551,1370,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,12,1855,0,3155,0,0]Al[4761,12,1856,1856,3156,0,0]Am[1374,13,1375,1375,129,113,0]An[1374,13,1375,1375,4006,113,0]An[1374,13,1551,1551,4037,113,0]An[1374,13,1551,1551,1370,113,0]An[1374,13,1551,1551,4426,113,0]Ak[499,13,500,500,4007,113,0]An[4043,13,4044,4044,5054,113,0]An[3974,13,3975,3730,4545,113,0]An[3974,13,3975,3730,2427,113,0]Ak[3974,13,3975,0,4279,113,0]An[3974,13,3975,1057,2428,113,0]An[3974,13,3975,1057,4704,113,0]An[3974,13,3975,3730,3323,113,0]An[3974,13,3975,3730,3325,113,0]An[3974,13,3975,3730,2006,113,0]An[3974,13,3975,3730,1589,113,0]An[3974,13,3975,3730,3127,113,0]An[3974,13,3975,3730,2401,113,0]An[3974,13,3975,3730,4181,113,0]An[3974,13,3975,3730,4186,113,0]An[3974,13,3975,3730,4967,113,0]An[3974,13,3975,3730,4805,113,0]An[3974,13,3975,2458,2413,113,0]An[3974,13,3975,2458,4288,113,0]An[3974,13,3975,2458,1570,113,0]An[3974,13,3975,2485,2417,113,0]An[3974,13,3975,2485,3353,113,0]An[3974,13,3975,2485,4405,113,0]Am[3974,13,3975,1057,482,113,0]An[3974,13,3975,1057,4431,113,0]An[3974,13,3975,4110,3350,113,0]An[3974,13,3975,4110,4906,113,0]An[3974,13,3975,4110,4424,113,0]An[3974,13,3975,4110,3165,113,0]An[3974,13,3975,4110,3352,113,0]An[3974,13,3975,4110,4423,113,0]An[3974,13,3975,1564,4196,113,0]An[3974,13,3975,1100,4032,113,0]An[3974,13,3975,4115,3356,113,0]An[3974,13,3975,4115,3370,113,0]An[3974,13,3975,4115,4195,113,0]An[3974,13,3975,4115,2020,113,0]Ak[3974,13,3975,0,3347,113,0]Ak[3974,13,3975,0,4319,113,0]Ak[3974,13,3975,0,4537,113,0]Ak[3974,13,3975,0,4173,113,0]Ak[3974,13,3975,0,4414,113,0]Ak[3974,13,3975,0,1613,113,0]Ak[3974,13,3975,0,4964,113,0]Ak[3974,13,3975,0,1333,113,0]Ak[3974,13,3975,0,1596,113,0]Ak[3974,13,3975,0,2448,113,0]Ak[3974,13,3975,0,3331,113,0]Ak[3974,13,3975,0,3162,113,0]Ak[3974,13,3975,0,4523,113,0]Ak[3974,13,3975,0,2030,113,0]Ak[3974,13,3975,0,4147,113,0]Ak[3974,13,3975,0,4519,113,0]Ak[3974,13,3975,0,4533,113,0]Ak[3974,13,3975,0,4540,113,0]Ak[3974,13,3975,0,4829,113,0]Ak[3974,13,3975,0,4844,113,0]Ak[3974,13,3975,0,4019,113,0]Ak[3974,13,3975,0,4400,113,0]Ak[3974,13,3975,0,4189,113,0]Ak[3974,13,3975,0,5021,113,0]Ak[3974,13,3975,0,4141,113,0]Ak[3974,13,3975,0,4009,113,0]Ak[3974,13,3975,0,3145,113,0]Ak[3974,13,3975,0,4516,113,0]Ak[3974,13,3975,0,4706,113,0]Ak[3974,13,3975,0,4153,113,0]Ak[3974,13,3975,0,4156,113,0]Ak[3974,13,3975,0,4161,113,0]Ak[3974,13,3975,0,4863,113,0]Ak[3974,13,3975,0,3334,113,0]Ak[3974,13,3975,0,4737,113,0]Aj[3974,13,3975,0,489,113,0]Aj[3974,13,3975,0,121,113,0]Ak[3974,13,3975,0,2436,113,0]Ak[3974,13,3975,0,2445,113,0]Aj[3974,13,3725,0,115,113,0]Ak[3974,13,3725,0,3134,113,0]Ak[3974,13,3725,0,1337,113,0]Ak[3974,13,3725,0,4014,113,0]Ak[3974,13,3725,0,2003,113,0]Ak[3974,13,3725,0,4307,113,0]Ak[3974,13,3725,0,4795,113,0]Ak[3974,13,1855,0,1998,113,0]Aj[3974,13,1855,0,476,113,0]Ak[3974,13,1855,0,5112,113,0]Ak[3974,13,1855,0,4012,113,0]Ak[3974,13,1855,0,4867,113,0]Ak[3974,13,1934,0,3365,113,0]Ak[3974,13,1934,0,4290,113,0]Ak[3974,13,1934,0,4733,113,0]Ak[3974,13,1934,0,4735,113,0]Ak[3974,13,1934,0,4960,113,0]Ak[3974,13,1934,0,1350,113,0]An[4761,13,4762,4550,4280,113,0]An[4761,13,4762,3190,3140,113,0]An[4761,13,4762,1083,1578,113,0]An[4761,13,4762,2164,2024,113,0]An[4761,13,4762,2164,3351,113,0]An[4761,13,4762,2164,3357,113,0]An[4761,13,4762,3731,3326,113,0]An[4761,13,4762,1079,3348,113,0]An[4761,13,4762,1853,4320,113,0]An[4761,13,4762,2459,2414,113,0]An[4761,13,4762,2459,4286,113,0]An[4761,13,4762,1918,5171,113,0]An[4761,13,4762,4328,5141,113,0]An[4761,13,4762,1563,4538,113,0]An[4761,13,4762,1563,4174,113,0]An[4761,13,4762,1563,4415,113,0]An[4761,13,4762,1971,1614,113,0]An[4761,13,4762,1971,4965,113,0]An[4761,13,4762,3181,1334,113,0]An[4761,13,4762,3190,4806,113,0]An[4761,13,4762,1537,1597,113,0]An[4761,13,4762,1537,4185,113,0]An[4761,13,4762,1537,3346,113,0]An[4761,13,4762,1537,3161,113,0]An[4761,13,4762,1537,4860,113,0]An[4761,13,4762,1537,3321,113,0]An[4761,13,4762,1553,2449,113,0]An[4761,13,4762,1553,3332,113,0]An[4761,13,4762,1553,3163,113,0]An[4761,13,4762,1553,4524,113,0]An[4761,13,4762,1083,3166,113,0]An[4761,13,4762,1083,4315,113,0]An[4761,13,4762,1088,2031,113,0]An[4761,13,4762,1088,3371,113,0]An[4761,13,4762,1088,4018,113,0]An[4761,13,4762,2164,4036,113,0]An[4761,13,4762,2164,4425,113,0]An[4761,13,4762,2164,4151,113,0]An[4761,13,4762,3731,4520,113,0]An[4761,13,4762,3731,3158,113,0]An[4761,13,4762,3731,4391,113,0]An[4761,13,4762,3731,5050,113,0]An[4761,13,4762,3731,1355,113,0]An[4761,13,4762,1079,4534,113,0]An[4761,13,4762,3308,4541,113,0]An[4761,13,4762,3308,4830,113,0]An[4761,13,4762,3308,4845,113,0]An[4761,13,4762,3308,4020,113,0]An[4761,13,4762,1853,4401,113,0]An[4761,13,4762,1853,4191,113,0]An[4761,13,4762,1853,5022,113,0]An[4761,13,4762,1853,4143,113,0]An[4761,13,4762,1853,4010,113,0]An[4761,13,4762,1527,3146,113,0]An[4761,13,4762,1527,4406,113,0]An[4761,13,4762,1527,3338,113,0]An[4761,13,4762,1527,3360,113,0]An[4761,13,4762,1527,4158,113,0]An[4761,13,4762,1527,4518,113,0]An[4761,13,4762,1527,4708,113,0]An[4761,13,4762,1527,2407,113,0]An[4761,13,4762,2459,4712,113,0]An[4761,13,4762,2459,2418,113,0]An[4761,13,4762,2459,4154,113,0]An[4761,13,4762,2459,4004,113,0]An[4761,13,4762,2459,3354,113,0]An[4761,13,4762,1918,4310,113,0]An[4761,13,4762,1918,4532,113,0]An[4761,13,4762,1918,5053,113,0]An[4761,13,4762,1918,2420,113,0]An[4761,13,4762,1918,4804,113,0]An[4761,13,4762,1945,4522,113,0]An[4761,13,4762,1945,4287,113,0]An[4761,13,4762,1945,4157,113,0]An[4761,13,4762,1945,4162,113,0]Ak[4761,13,4762,0,4429,113,0]Ak[4761,13,4762,0,4864,113,0]An[4761,13,4762,4328,3335,113,0]An[4761,13,4762,4328,5146,113,0]An[4761,13,4762,4895,4738,113,0]Am[4761,13,4762,4762,490,113,0]Am[4761,13,4762,4762,122,113,0]An[4761,13,4762,4762,2437,113,0]An[4761,13,4762,4762,2446,113,0]An[4761,13,4762,4762,3144,113,0]An[4761,13,1851,1851,4321,113,0]An[4761,13,5006,5006,4905,113,0]An[4761,13,5006,5006,1352,113,0]An[4761,13,1856,1856,1999,113,0]Am[4761,13,1856,1856,477,113,0]An[4761,13,1856,1856,5113,113,0]An[4761,13,1856,1856,4013,113,0]An[4761,13,1856,1856,4870,113,0]An[4761,13,1935,1935,3366,113,0]An[4761,13,1935,1935,4291,113,0]An[4761,13,1935,1935,4734,113,0]An[4761,13,1935,1935,4736,113,0]An[4761,13,1935,1935,4961,113,0]Ak[4761,13,1935,0,1351,113,0]Al[4761,13,3726,137,116,113,0]Am[4761,13,3726,137,3135,113,0]An[4761,13,3726,1522,1338,113,0]An[4761,13,3726,1522,4015,113,0]An[4761,13,3726,2507,2004,113,0]An[4761,13,3726,2507,4308,113,0]An[4761,13,3726,3726,4796,113,0]An[4761,13,2135,2135,1353,113,0]An[4761,13,2135,2135,1356,113,0]An[4465,13,4466,4466,3369,113,0]Am[4465,13,4466,397,4430,113,0]An[4465,13,4466,4466,2434,113,0]An[4465,13,4466,4466,2447,113,0]An[4465,13,4466,4466,4163,113,0]An[1058,13,1059,1059,3137,113,0]An[1058,13,1059,1059,4899,113,0]Am[1058,13,1845,1845,130,113,0]An[1058,13,1552,1552,1371,113,0]An[1058,13,1552,1552,4038,113,0]An[1058,13,1552,1552,4528,113,0]An[1058,13,4248,4248,5098,113,0]An[1058,13,4248,4248,5099,113,0]Am[1058,13,1940,1940,131,113,0]Am[3994,13,3995,3995,120,113,0]An[3994,13,3995,3995,2404,113,0]Al[3974,13,3975,3730,4967,0,0]Al[3974,13,3975,1057,4431,0,0]Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4143,0,0]Al[4761,14,4762,1918,5053,0,0]Ai[4761,14,4803,0,4865,0,0]Ah[4761,14,4531,0,478,0,0]Ai[4761,14,4531,0,1365,0,0]Ah[3974,5,3975,3730,0,0,0]Al[1374,15,1375,1375,4006,0,0]Ae[3994,6,3995,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1527,0,0,0]Al[3974,15,3975,3730,4805,0,0]Al[4761,15,4762,3190,4806,0,0]Ah[3974,5,3975,2458,0,0,0]Ah[4761,5,4762,2459,0,0,0]Al[4761,15,4762,2164,4036,0,0]Ah[3974,5,3975,2485,0,0,0]2Ah[4761,6,4762,1918,0,0,0]Ai[3974,19,3975,0,1613,0,0]Al[4761,19,4762,1971,1614,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]=Ai[3974,12,1855,0,4190,0,0]>Al[3974,13,1934,0,4735,4190,0]>Al[4761,12,1856,1856,4192,0,0]>Ao[4761,13,1935,1935,4736,4192,0]Ao[1058,13,4248,4248,5099,4192,0]An[3994,13,3995,3995,120,4021,0]Ao[3994,13,3995,3995,2404,4021,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1553,3163,0,0]Ai[3974,13,3975,0,4189,0,0]Al[4761,13,4762,1853,4191,0,0]Al[3974,13,3975,2156,4388,0,0]0>=Al[1058,13,1552,1552,4528,0,0]Ae[1058,2,1059,0,0,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2459,2418,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]43Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Al[4465,14,4466,4466,2447,0,0]Al[4761,13,4762,2164,2024,0,0]Al[4761,13,4762,4328,5141,0,0]Al[4761,13,4762,4328,5146,0,0]Al[3974,13,3975,4115,3356,0,0]Al[4761,13,4762,2164,3357,0,0]<;Al[1374,14,1551,1551,1370,0,0]Ai[499,14,500,500,4007,0,0]Ai[3974,13,1934,0,4733,0,0]Ai[3974,13,1934,0,4735,0,0]Al[4761,13,1935,1935,4734,0,0]Al[4761,13,1935,1935,4736,0,0]76Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Ah[3974,5,3975,4550,0,0,0]Ah[4761,5,4762,4550,0,0,0]Ai[3974,15,3975,0,4519,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,3731,4520,0,0]Al[4761,15,4762,2459,4154,0,0]Ah[4761,6,4762,2459,0,0,0]Ah[4761,5,4762,1945,0,0,0]Ah[3974,5,3975,2458,0,0,0]Al[3974,15,3975,4110,4423,0,0]Ah[3974,5,1934,1935,0,0,0]Ae[4761,5,1935,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Al[4761,15,4762,1918,4804,0,0]>Al[4761,15,4762,1537,3161,0,0]=5454Al[4761,15,4762,2459,4286,0,0]Ah[3974,5,3725,2507,0,0,0]Ah[4761,5,3726,2507,0,0,0]Ah[4761,5,4762,1918,0,0,0]98Ah[4761,6,4762,1083,0,0,0]:9Ah[3974,5,3975,1853,0,0,0]Ah[4761,5,4762,1853,0,0,0]Ae[4761,5,1851,0,0,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]Al[1058,14,1059,1059,3137,0,0]Ae[4761,2,4762,0,0,0,0]Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Al[3974,12,3975,2156,4388,0,0]Ab[4331,3,0,0,0,0,0]Ak[1374,14,1375,1375,129,0,0]Al[1374,14,1551,1551,4037,0,0]98Al[3974,13,3975,2485,3353,0,0]Al[4761,13,4762,2459,3354,0,0]Ai[3974,13,3975,0,4829,0,0]Al[4761,13,4762,3308,4830,0,0];=Al[3974,13,3975,4110,4906,0,0]Al[4761,13,4762,2164,3351,0,0]Al[3974,13,3975,3730,3323,0,0]Al[3974,13,3975,3730,3325,0,0]Al[3974,13,3975,2485,2417,0,0]Ak[3974,13,3975,1057,482,0,0]Al[4761,13,4762,2459,2418,0,0]<;Ai[3974,13,3975,0,1613,0,0]Ai[3974,13,3975,0,1596,0,0]Al[4761,13,4762,1971,1614,0,0]Al[4761,13,4762,1537,1597,0,0]20Ae[1374,7,1551,0,0,0,0]?>Al[1374,14,1551,1551,1370,0,0]Ao[1058,13,1059,1059,4899,3170,0]Ai[3974,15,3975,0,3145,0,0]Ah[3974,5,3975,1527,0,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1527,0,0,0]Ah[3974,6,3975,3731,0,0,0]Ah[4761,6,4762,3731,0,0,0]Al[4761,15,4762,2459,4286,0,0]Ah[4761,5,4762,1945,0,0,0]Ah[3974,5,3975,1553,0,0,0]Ah[4761,5,4762,1553,0,0,0]97Ae[1058,5,1552,0,0,0,0]:8Ah[4761,6,4762,1918,0,0,0]Ah[3974,5,3975,1079,0,0,0]Ah[4761,5,4762,1079,0,0,0]Ah[3974,5,3975,1563,0,0,0]Ah[4761,5,4762,1563,0,0,0]Ah[3974,5,3975,3308,0,0,0]Ah[4761,5,4762,3308,0,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,3731,4520,0,0]Ah[3974,5,3975,3730,0,0,0]Al[3974,15,3975,4110,3165,0,0]Al[3974,15,3975,4110,3352,0,0]Al[3974,15,3975,4115,3370,0,0]Al[3974,15,3975,4115,4195,0,0]An[1374,13,1375,1375,129,4285,0]Ao[1374,13,1375,1375,4006,4285,0]Ao[3974,13,3975,3730,3325,4285,0]Ao[3974,13,3975,3730,4967,4285,0]Ao[3974,13,3975,3730,4805,4285,0]Ao[3974,13,3975,2458,2413,4285,0]Ao[3974,13,3975,2458,4288,4285,0]An[3974,13,3975,1057,482,4285,0]Ao[3974,13,3975,1057,4431,4285,0]Ao[3974,13,3975,4110,4424,4285,0]Ao[3974,13,3975,4110,3165,4285,0]Ao[3974,13,3975,4110,3352,4285,0]Ao[3974,13,3975,4110,4423,4285,0]Ao[3974,13,3975,4115,3370,4285,0]Ao[3974,13,3975,4115,4195,4285,0]Al[3974,13,3975,0,1613,4285,0]Al[3974,13,3975,0,4964,4285,0]Al[3974,13,3975,0,1596,4285,0]Al[3974,13,3975,0,2448,4285,0]Al[3974,13,3975,0,3331,4285,0]Al[3974,13,3975,0,3162,4285,0]Al[3974,13,3975,0,4523,4285,0]Al[3974,13,3975,0,2030,4285,0]Al[3974,13,3975,0,4147,4285,0]Al[3974,13,3975,0,4519,4285,0]Al[3974,13,3975,0,4540,4285,0]Al[3974,13,3975,0,4829,4285,0]Al[3974,13,3975,0,4844,4285,0]Al[3974,13,3975,0,4019,4285,0]Al[3974,13,3975,0,4400,4285,0]Al[3974,13,3975,0,4189,4285,0]Al[3974,13,3975,0,5021,4285,0]Al[3974,13,3975,0,4141,4285,0]Al[3974,13,3975,0,4009,4285,0]Al[3974,13,3975,0,3145,4285,0]Al[3974,13,3975,0,4516,4285,0]Al[3974,13,3975,0,4706,4285,0]Al[3974,13,3975,0,4153,4285,0]Al[3974,13,3975,0,4156,4285,0]Al[3974,13,3975,0,4161,4285,0]Al[3974,13,3975,0,4863,4285,0]Al[3974,13,3975,0,3334,4285,0]Ak[3974,13,3975,0,489,4285,0]Ak[3974,13,3975,0,121,4285,0]Al[3974,13,3975,0,2436,4285,0]Ak[3974,13,3725,0,115,4285,0]Al[3974,13,3725,0,3134,4285,0]Al[3974,13,3725,0,1337,4285,0]Al[3974,13,3725,0,4014,4285,0]Al[3974,13,3725,0,2003,4285,0]Al[3974,13,3725,0,4307,4285,0]Al[3974,13,1934,0,4960,4285,0]Al[3974,13,1934,0,1350,4285,0]Ao[4761,13,4762,1971,1614,4285,0]Ao[4761,13,4762,1971,4965,4285,0]Ao[4761,13,4762,3190,4806,4285,0]Ao[4761,13,4762,1537,1597,4285,0]Ao[4761,13,4762,1537,4185,4285,0]Ao[4761,13,4762,1537,3346,4285,0]Ao[4761,13,4762,1537,3161,4285,0]Ao[4761,13,4762,1537,4860,4285,0]Ao[4761,13,4762,1537,3321,4285,0]Ao[4761,13,4762,1553,2449,4285,0]Ao[4761,13,4762,1553,3332,4285,0]Ao[4761,13,4762,1553,3163,4285,0]Ao[4761,13,4762,1553,4524,4285,0]Ao[4761,13,4762,1083,3166,4285,0]Ao[4761,13,4762,1083,4315,4285,0]Ao[4761,13,4762,1088,2031,4285,0]Ao[4761,13,4762,1088,3371,4285,0]Ao[4761,13,4762,1088,4018,4285,0]Ao[4761,13,4762,2164,4425,4285,0]Ao[4761,13,4762,2164,4151,4285,0]Ao[4761,13,4762,3731,4520,4285,0]Ao[4761,13,4762,3731,3158,4285,0]Ao[4761,13,4762,3731,4391,4285,0]Ao[4761,13,4762,3731,5050,4285,0]Ao[4761,13,4762,3308,4541,4285,0]Ao[4761,13,4762,3308,4830,4285,0]Ao[4761,13,4762,3308,4845,4285,0]Ao[4761,13,4762,3308,4020,4285,0]Ao[4761,13,4762,1853,4401,4285,0]Ao[4761,13,4762,1853,4191,4285,0]Ao[4761,13,4762,1853,5022,4285,0]Ao[4761,13,4762,1853,4143,4285,0]Ao[4761,13,4762,1853,4010,4285,0]Ao[4761,13,4762,1527,3146,4285,0]Ao[4761,13,4762,1527,3338,4285,0]Ao[4761,13,4762,1527,3360,4285,0]Ao[4761,13,4762,1527,4158,4285,0]Ao[4761,13,4762,1527,4518,4285,0]Ao[4761,13,4762,1527,4708,4285,0]Ao[4761,13,4762,1527,2407,4285,0]Ao[4761,13,4762,2459,4712,4285,0]Ao[4761,13,4762,2459,4154,4285,0]Ao[4761,13,4762,1918,4532,4285,0]Ao[4761,13,4762,1918,5053,4285,0]Ao[4761,13,4762,1918,2420,4285,0]Ao[4761,13,4762,1918,4804,4285,0]Ao[4761,13,4762,1945,4157,4285,0]Ao[4761,13,4762,1945,4162,4285,0]Al[4761,13,4762,0,4429,4285,0]Al[4761,13,4762,0,4864,4285,0]Ao[4761,13,4762,4328,3335,4285,0]Ao[4761,13,4762,4328,5146,4285,0]An[4761,13,4762,4762,490,4285,0]An[4761,13,4762,4762,122,4285,0]Ao[4761,13,4762,4762,2437,4285,0]Ao[4761,13,4762,4762,3144,4285,0]Ao[4761,13,1851,1851,4321,4285,0]Ao[4761,13,5006,5006,4905,4285,0]Ao[4761,13,5006,5006,1352,4285,0]Ao[4761,13,1935,1935,4961,4285,0]Al[4761,13,1935,0,1351,4285,0]Am[4761,13,3726,137,116,4285,0]An[4761,13,3726,137,3135,4285,0]Ao[4761,13,3726,1522,1338,4285,0]Ao[4761,13,3726,1522,4015,4285,0]Ao[4761,13,3726,2507,2004,4285,0]Ao[4761,13,3726,2507,4308,4285,0]Ao[4761,13,2135,2135,1356,4285,0]Ao[4465,13,4466,4466,2434,4285,0]Ao[4465,13,4466,4466,2447,4285,0]Ao[4465,13,4466,4466,4163,4285,0]Ao[1058,13,1059,1059,4899,4285,0]Ao[1058,13,1552,1552,4528,4285,0]Ah[3974,7,3975,2156,0,0,0]Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Ai[3974,12,1855,0,3155,0,0]Al[4761,12,1856,1856,3156,0,0]Al[4465,14,4466,4466,2434,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]10Ae[1058,7,1059,0,0,0,0]Al[3974,13,3975,2458,2413,0,0]Ae[4331,7,4332,0,0,0,0]Ae[1374,7,1375,0,0,0,0]Al[4761,13,4762,3731,3326,0,0]Al[3974,13,3975,3730,3325,0,0]1Al[4761,13,4762,2164,4425,0,0]Al[1374,13,1551,1551,1370,0,0]Al[1374,14,1551,1551,4037,0,0]Al[3974,12,3975,2156,2015,0,0]Ao[3974,13,3975,1564,4196,2015,0]Ao[3974,13,3975,1100,4032,2015,0]Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Ah[3974,5,3975,1057,0,0,0]Ai[3974,15,3975,0,3145,0,0]Ah[3974,5,3975,1527,0,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1527,0,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,3731,4520,0,0]Ah[4761,5,4762,2459,0,0,0]Al[4761,15,4762,3731,1355,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,2459,4154,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]32Al[4761,15,4762,3731,3158,0,0]><><><21Ah[3974,5,1934,1935,0,0,0]Ae[4761,5,1935,0,0,0,0]10Ah[3974,5,3975,4895,0,0,0]Ah[4761,5,4762,4895,0,0,0]Al[4761,15,2135,2135,1353,0,0]5Al[4761,15,4762,1918,4532,0,0]Ai[3974,12,3975,0,2441,0,0]Al[3974,13,3975,0,4279,2441,0]Al[3974,13,3975,0,4173,2441,0]Al[4761,12,4762,1563,2442,0,0]Ao[4761,13,4762,4550,4280,2442,0]Ao[4761,13,4762,1563,4174,2442,0]Ae[4043,7,4044,0,0,0,0]Al[3974,13,3975,3730,3325,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]32An[1374,13,1375,1375,129,4402,0]Ao[1058,13,1059,1059,3137,4402,0]An[1058,13,1845,1845,130,4402,0]An[1058,13,1940,1940,131,4402,0]An[3994,13,3995,3995,120,4402,0]Ao[3994,13,3995,3995,2404,4402,0]Ab[4761,3,0,0,0,0,0]Ak[4465,13,4466,397,4430,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0];:Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,2459,2414,0,0]?>32Ae[4331,7,4332,0,0,0,0]5Al[1058,14,1059,1059,3137,0,0]60Ae[1374,7,1375,0,0,0,0]Ai[3974,13,3975,0,4189,0,0]Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4191,0,0]Al[4761,13,4762,1853,4143,0,0]Ai[3974,14,3975,0,4189,0,0]Al[4761,14,4762,1853,4191,0,0]53Al[3974,13,3975,4115,3356,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,3731,4520,0,0]Ai[3974,10,3725,3726,0,0,0]Af[4761,10,3726,0,0,0,0]Ah[3974,5,3725,3726,0,0,0]Ae[4761,5,3726,0,0,0,0]Ai[3974,10,1855,1856,0,0,0]Af[4761,10,1856,0,0,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,2459,4154,0,0]Ai[3974,10,3975,3731,0,0,0]Ai[4761,10,4762,3731,0,0,0]Ah[4761,6,4762,1918,0,0,0]Ah[3974,6,3975,3730,0,0,0]Ah[4761,6,4762,3190,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]10Al[3974,15,3975,3730,4805,0,0]Al[4761,15,4762,3190,4806,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1553,3163,0,0]Ai[3974,14,3725,0,4795,0,0]Al[4761,14,3726,3726,4796,0,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]10Al[4761,13,4762,3190,3140,0,0]Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,2459,2414,0,0]Ab[4824,3,0,0,0,0,0]Ao[1058,13,1059,1059,4899,3170,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ah[3974,5,3975,3308,0,0,0]Ah[4761,5,4762,3308,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]54Ai[4761,10,4762,1918,0,0,0]Al[4761,15,4762,3731,1355,0,0]7600Al[4761,15,4762,1083,4315,0,0]4365Al[4761,15,2135,2135,1353,0,0]Ae[1058,9,1059,0,0,0,0]:9Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]<;<;Ah[4761,6,4762,1537,0,0,0]21Ae[3974,5,3975,0,0,0,0]Ae[4761,5,4762,0,0,0,0]Ai[3974,15,3975,0,4153,0,0]Ah[3974,5,1855,1856,0,0,0]Al[4761,15,4762,2459,4154,0,0]Al[4761,15,4762,1918,4804,0,0]Ae[4761,5,1856,0,0,0,0]An[1374,13,1375,1375,129,4392,0]Ao[1058,13,1059,1059,3137,4392,0]An[1058,13,1845,1845,130,4392,0]An[1058,13,1940,1940,131,4392,0]An[3994,13,3995,3995,120,4392,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Ai[3974,13,3975,0,1613,0,0]Al[4761,13,4762,1971,1614,0,0]Al[3974,14,3975,2458,4288,0,0]Al[3974,13,3975,3730,4967,0,0]Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Ai[3974,13,3975,0,4279,0,0]Al[4761,13,4762,4550,4280,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]7Al[4761,13,4762,3731,3326,0,0]Al[3974,13,3975,3730,3325,0,0]0176Ao[4043,13,4044,4044,5054,4858,0]Ai[3974,13,3975,0,4533,0,0]Al[4761,13,4762,1079,4534,0,0]Ae[1058,6,1059,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]10Al[4761,15,4762,3731,3158,0,0]Ae[4761,5,5006,0,0,0,0]Ah[3974,5,3975,4110,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Al[4761,15,2135,2135,1353,0,0]Al[3974,14,3975,2458,4288,0,0]Ak[3974,13,3975,0,489,3128,0]Ak[3974,13,3975,0,121,3128,0]An[4761,13,4762,4762,490,3128,0]An[4761,13,4762,4762,122,3128,0]Ao[3994,13,3995,3995,2404,3361,0]Ai[3974,13,3975,0,4844,0,0]Al[4761,13,4762,3308,4845,0,0]Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,12,3975,0,4801,0,0]Al[4761,12,4762,3731,4802,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1553,3163,0,0]Ai[3974,12,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,12,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Al[3974,14,3975,2458,4288,0,0]Al[4761,13,4762,2164,2024,0,0]Ah[4761,2,4326,4114,0,0,0]Ae[1374,7,1375,0,0,0,0]==Ak[3974,13,3975,1057,482,0,0]Ak[4465,13,4466,397,4430,0,0]032Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Ae[4761,2,4762,0,0,0,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Ai[3974,10,3975,4328,0,0,0]Ai[4761,10,4762,4328,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,2459,4154,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ah[3974,5,3975,1853,0,0,0]Ah[4761,5,4762,1853,0,0,0]Ai[3974,12,1855,0,3155,0,0]Al[4761,12,1856,1856,3156,0,0]Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,3731,3326,0,0]Al[4761,13,4762,2459,2414,0,0]Al[4761,13,4762,4762,3144,0,0]Ai[3974,13,3725,0,4307,0,0]Al[4761,13,3726,2507,4308,0,0]Al[3974,13,3975,2156,3168,0,0]0Al[3974,13,3975,3730,3325,0,0]6Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Al[1058,14,1059,1059,3137,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Al[4761,15,2135,2135,1353,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]10Al[4761,15,4762,1537,4860,0,0]Al[4761,15,4762,3731,3158,0,0]Al[4761,15,4762,3731,4391,0,0]Ah[4761,5,4762,3731,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1918,0,0,0]Ae[4043,5,4044,0,0,0,0]Al[1058,13,1059,1059,3137,0,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Ah[4761,7,4940,4939,0,0,0]0Al[4761,13,4762,3731,3326,0,0]6Ak[3974,13,3975,1057,482,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]Ao[4043,13,4044,4044,5054,4858,0]0Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4143,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]10Ai[3974,13,3975,0,4829,0,0]Al[4761,13,4762,3308,4830,0,0]Am[1374,13,1375,1375,129,464,0]An[1058,13,1059,1059,3137,464,0]Am[1058,13,1845,1845,130,464,0]Am[1058,13,1940,1940,131,464,0]Am[3994,13,3995,3995,120,464,0]??>An[1374,13,1375,1375,129,4402,0]Ao[1058,13,1059,1059,3137,4402,0]An[1058,13,1845,1845,130,4402,0]An[1058,13,1940,1940,131,4402,0]An[3994,13,3995,3995,120,4402,0]Ao[3994,13,3995,3995,2404,4402,0]Ai[3974,13,3975,0,4189,0,0]Al[4761,13,4762,1853,4191,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ae[1058,5,4248,0,0,0,0]0Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4143,0,0]Al[3974,13,3975,2458,2413,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2459,2418,0,0]Al[4761,13,4762,3731,3326,0,0]Ao[4043,13,4044,4044,5054,4858,0];:Ah[3974,5,1855,1856,0,0,0]Ae[4761,5,1856,0,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]?>?>Al[3974,13,3975,3730,3325,0,0]6Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Ae[4043,7,4044,0,0,0,0]Ae[4331,7,4332,0,0,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1553,3332,0,0]?=Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]2=<==<>=>=>=<;<;Al[4761,14,5006,5006,4905,0,0]?>Ai[4761,18,4762,3308,0,0,0]") \ No newline at end of file +rd_("Ah[3974,15,3975,0,121,0,0]Ak[4761,15,4762,4762,122,0,0]Ai[3974,15,3975,0,4161,0,0]Al[4761,15,4762,1945,4162,0,0]Al[4465,15,4466,4466,4163,0,0]Al[4761,15,1851,1851,1603,0,0]Ao[1374,13,1375,1375,4006,3157,0]Ao[3974,13,3975,3730,4805,3157,0]An[3974,13,3975,1057,482,3157,0]Ao[3974,13,3975,1057,4431,3157,0]Ao[3974,13,3975,4110,4424,3157,0]Ao[3974,13,3975,4110,3165,3157,0]Ao[3974,13,3975,4110,3352,3157,0]Ao[3974,13,3975,4110,4423,3157,0]Ao[3974,13,3975,4115,3370,3157,0]Ao[3974,13,3975,4115,4195,3157,0]Al[3974,13,3975,0,1613,3157,0]Al[3974,13,3975,0,4964,3157,0]Al[3974,13,3975,0,1333,3157,0]Al[3974,13,3975,0,1596,3157,0]Al[3974,13,3975,0,2448,3157,0]Al[3974,13,3975,0,3162,3157,0]Al[3974,13,3975,0,2030,3157,0]Al[3974,13,3975,0,4147,3157,0]Al[3974,13,3975,0,4519,3157,0]Al[3974,13,3975,0,4019,3157,0]Al[3974,13,3975,0,4009,3157,0]Al[3974,13,3975,0,3145,3157,0]Al[3974,13,3975,0,4516,3157,0]Al[3974,13,3975,0,4706,3157,0]Al[3974,13,3975,0,4153,3157,0]Al[3974,13,3975,0,4156,3157,0]Al[3974,13,3975,0,4161,3157,0]Al[3974,13,3975,0,4863,3157,0]Ak[3974,13,3975,0,489,3157,0]Ak[3974,13,3975,0,121,3157,0]Al[3974,13,3975,0,2436,3157,0]Al[3974,13,1855,0,5112,3157,0]Al[3974,13,1934,0,1350,3157,0]Ao[4761,13,4762,1971,1614,3157,0]Ao[4761,13,4762,1971,4965,3157,0]Ao[4761,13,4762,3181,1334,3157,0]Ao[4761,13,4762,3190,4806,3157,0]Ao[4761,13,4762,1537,1597,3157,0]Ao[4761,13,4762,1537,3346,3157,0]Ao[4761,13,4762,1537,3161,3157,0]Ao[4761,13,4762,1537,4860,3157,0]Ao[4761,13,4762,1537,3321,3157,0]Ao[4761,13,4762,1552,2449,3157,0]Ao[4761,13,4762,1552,3163,3157,0]Ao[4761,13,4762,1083,3166,3157,0]Ao[4761,13,4762,1083,4315,3157,0]Ao[4761,13,4762,1088,2031,3157,0]Ao[4761,13,4762,1088,3371,3157,0]Ao[4761,13,4762,1088,4018,3157,0]Ao[4761,13,4762,2164,4036,3157,0]Ao[4761,13,4762,2164,4425,3157,0]Ao[4761,13,4762,2164,4151,3157,0]Ao[4761,13,4762,3731,4520,3157,0]Ao[4761,13,4762,3731,3158,3157,0]Ao[4761,13,4762,3731,4391,3157,0]Ao[4761,13,4762,3308,4020,3157,0]Ao[4761,13,4762,1853,4010,3157,0]Ao[4761,13,4762,1527,3146,3157,0]Ao[4761,13,4762,1527,4406,3157,0]Ao[4761,13,4762,1527,3338,3157,0]Ao[4761,13,4762,1527,3360,3157,0]Ao[4761,13,4762,1527,4158,3157,0]Ao[4761,13,4762,1527,4518,3157,0]Ao[4761,13,4762,1527,4708,3157,0]Ao[4761,13,4762,2459,4712,3157,0]Ao[4761,13,4762,2459,4154,3157,0]Ao[4761,13,4762,1918,2420,3157,0]Ao[4761,13,4762,1918,4804,3157,0]Ao[4761,13,4762,1945,4157,3157,0]Ao[4761,13,4762,1945,4162,3157,0]Al[4761,13,4762,0,4429,3157,0]Al[4761,13,4762,0,4864,3157,0]An[4761,13,4762,4762,490,3157,0]An[4761,13,4762,4762,122,3157,0]Ao[4761,13,4762,4762,2437,3157,0]Ao[4761,13,4762,4762,3144,3157,0]Ao[4761,13,1851,1851,4321,3157,0]Ao[4761,13,5006,5006,4905,3157,0]Ao[4761,13,5006,5006,1352,3157,0]Ao[4761,13,1856,1856,5113,3157,0]Al[4761,13,1935,0,1351,3157,0]Ao[4465,13,4466,4466,4163,3157,0]Ak[3974,13,3975,1057,482,0,0]Al[3974,13,3975,1057,4431,0,0]Al[3974,13,3975,4110,3350,0,0]Al[3974,13,3975,4110,4906,0,0]Al[3974,13,3975,4115,3356,0,0]Ai[4761,14,3159,0,1590,0,0]Ai[4761,14,3159,0,3125,0,0]Ai[4761,14,3159,0,4298,0,0]Ai[4761,14,3159,0,4182,0,0]Ai[4761,14,3159,0,4175,0,0]Ai[4761,14,4035,0,2430,0,0]Ai[4761,14,4035,0,2451,0,0]Ai[4761,14,4035,0,2415,0,0]Ai[4761,14,4035,0,2018,0,0]Ae[1374,7,1375,0,0,0,0]Ah[4761,14,4531,0,478,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ag[3974,5,3725,137,0,0,0]Ag[4761,5,3726,137,0,0,0]Al[1058,15,1059,1059,4899,0,0]Ae[3994,5,3995,0,0,0,0]Ah[3974,6,3975,4762,0,0,0]Ae[4761,6,4762,0,0,0,0]Al[4761,15,1851,1851,1603,0,0]Ai[3974,19,3975,0,1613,0,0]Ai[3974,19,3975,0,1596,0,0]Al[4761,19,4762,1971,1614,0,0]Al[4761,19,4762,1537,1597,0,0]Ae[1374,5,1375,0,0,0,0]Ae[1058,5,1845,0,0,0,0]Ae[1058,5,1940,0,0,0,0]Ah[3974,15,3975,0,121,0,0]Ak[4761,15,4762,4762,122,0,0]Ak[3974,13,3975,0,1613,132,0]Ak[3974,13,3975,0,1596,132,0]Ak[3974,13,3975,0,4019,132,0]Ak[3974,13,3975,0,4156,132,0]Aj[3974,13,3975,0,489,132,0]Aj[3974,13,3975,0,121,132,0]Ak[3974,13,3975,0,2436,132,0]An[4761,13,4762,1971,1614,132,0]An[4761,13,4762,3190,4806,132,0]An[4761,13,4762,1537,1597,132,0]An[4761,13,4762,1083,3166,132,0]An[4761,13,4762,1083,4315,132,0]An[4761,13,4762,1088,3371,132,0]An[4761,13,4762,3308,4020,132,0]An[4761,13,4762,2459,4712,132,0]An[4761,13,4762,1918,4804,132,0]An[4761,13,4762,1945,4157,132,0]Am[4761,13,4762,4762,490,132,0]Am[4761,13,4762,4762,122,132,0]An[4761,13,4762,4762,2437,132,0]Ai[3974,13,3975,0,2436,0,0]Al[4761,13,4762,4762,2437,0,0]Ai[4761,14,4035,0,1591,0,0]Ai[4761,14,4035,0,3131,0,0]Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Al[4465,14,4466,4466,2434,0,0]Al[4465,14,4466,4466,2447,0,0]An[1374,13,1375,1375,129,1340,0]Ao[1374,13,1375,1375,4006,1340,0]Ao[1374,13,1551,1551,4426,1340,0]Ao[4043,13,4044,4044,5054,1340,0]Ao[3974,13,3975,3730,3323,1340,0]Ao[3974,13,3975,3730,3325,1340,0]Ao[3974,13,3975,3730,4967,1340,0]Db[3974,13,3975,3730,4805,1340,0,\"impl-Debug-for-SendDatagramError\"]Dd[3974,13,3975,3730,4805,1990,0,\"impl-Display-for-SendDatagramError\"]Ao[3974,13,3975,2458,2413,1340,0]Ao[3974,13,3975,2458,4288,1340,0]Ao[3974,13,3975,2485,2417,1340,0]Ck[3974,13,3975,2485,3353,1340,0,\"impl-Debug-for-RetryError\"]Cm[3974,13,3975,2485,3353,1990,0,\"impl-Display-for-RetryError\"]Ao[3974,13,3975,2485,4405,1340,0]An[3974,13,3975,1057,482,1340,0]Ao[3974,13,3975,1057,4431,1340,0]Ao[3974,13,3975,4110,3350,1340,0]Ao[3974,13,3975,4110,4906,1340,0]Da[3974,13,3975,4110,4424,1990,0,\"impl-Display-for-ReadToEndError\"]Co[3974,13,3975,4110,4424,1340,0,\"impl-Debug-for-ReadToEndError\"]Cl[3974,13,3975,4110,3165,1990,0,\"impl-Display-for-ReadError\"]Cj[3974,13,3975,4110,3165,1340,0,\"impl-Debug-for-ReadError\"]Cm[3974,13,3975,4110,3352,1990,0,\"impl-Display-for-ResetError\"]Ck[3974,13,3975,4110,3352,1340,0,\"impl-Debug-for-ResetError\"]Co[3974,13,3975,4110,4423,1340,0,\"impl-Debug-for-ReadExactError\"]Da[3974,13,3975,4110,4423,1990,0,\"impl-Display-for-ReadExactError\"]Ao[3974,13,3975,1564,4196,1340,0]Ao[3974,13,3975,1100,4032,1340,0]Ao[3974,13,3975,4115,3356,1340,0]Cm[3974,13,3975,4115,3370,1990,0,\"impl-Display-for-WriteError\"]Ck[3974,13,3975,4115,3370,1340,0,\"impl-Debug-for-WriteError\"]Co[3974,13,3975,4115,4195,1990,0,\"impl-Display-for-StoppedError\"]Cm[3974,13,3975,4115,4195,1340,0,\"impl-Debug-for-StoppedError\"]Ao[3974,13,3975,4115,2020,1340,0]Cd[3974,13,3975,0,1613,1340,0,\"impl-Debug-for-VarInt\"]Cf[3974,13,3975,0,1613,1990,0,\"impl-Display-for-VarInt\"]Dd[3974,13,3975,0,4964,1990,0,\"impl-Display-for-VarIntBoundsExceeded\"]Db[3974,13,3975,0,4964,1340,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Al[3974,13,3975,0,1333,1340,0]Cf[3974,13,3975,0,1596,1990,0,\"impl-Display-for-PathId\"]Cd[3974,13,3975,0,1596,1340,0,\"impl-Debug-for-PathId\"]Al[3974,13,3975,0,2448,1340,0]Al[3974,13,3975,0,3331,1340,0]Al[3974,13,3975,0,3162,1340,0]Al[3974,13,3975,0,4523,1340,0]Al[3974,13,3975,0,2030,1340,0]Cl[3974,13,3975,0,4147,1990,0,\"impl-Display-for-ClosedStream\"]Cj[3974,13,3975,0,4147,1340,0,\"impl-Debug-for-ClosedStream\"]Co[3974,13,3975,0,4519,1990,0,\"impl-Display-for-ConnectionError\"]Cm[3974,13,3975,0,4519,1340,0,\"impl-Debug-for-ConnectionError\"]Al[3974,13,3975,0,4533,1340,0]Al[3974,13,3975,0,4540,1340,0]Al[3974,13,3975,0,4829,1340,0]Al[3974,13,3975,0,4844,1340,0]Al[3974,13,3975,0,4019,1340,0]Al[3974,13,3975,0,4400,1340,0]Al[3974,13,3975,0,4189,1340,0]Al[3974,13,3975,0,5021,1340,0]Al[3974,13,3975,0,4141,1340,0]Ck[3974,13,3975,0,4009,1990,0,\"impl-Display-for-ConfigError\"]Ci[3974,13,3975,0,4009,1340,0,\"impl-Debug-for-ConfigError\"]Ci[3974,13,3975,0,3145,1990,0,\"impl-Display-for-FrameType\"]Cg[3974,13,3975,0,3145,1340,0,\"impl-Debug-for-FrameType\"]Cm[3974,13,3975,0,4516,1340,0,\"impl-Debug-for-ConnectionClose\"]Co[3974,13,3975,0,4516,1990,0,\"impl-Display-for-ConnectionClose\"]Cn[3974,13,3975,0,4706,1340,0,\"impl-Debug-for-ApplicationClose\"]D`[3974,13,3975,0,4706,1990,0,\"impl-Display-for-ApplicationClose\"]Cl[3974,13,3975,0,4153,1990,0,\"impl-Display-for-ConnectError\"]Cj[3974,13,3975,0,4153,1340,0,\"impl-Debug-for-ConnectError\"]Cj[3974,13,3975,0,4156,1340,0,\"impl-Debug-for-ConnectionId\"]Cl[3974,13,3975,0,4156,1990,0,\"impl-Display-for-ConnectionId\"]Al[3974,13,3975,0,4161,1340,0]Db[3974,13,3975,0,4863,1990,0,\"impl-Display-for-TransportErrorCode\"]D`[3974,13,3975,0,4863,1340,0,\"impl-Debug-for-TransportErrorCode\"]Al[3974,13,3975,0,3334,1340,0]Al[3974,13,3975,0,4737,1340,0]Ak[3974,13,3975,0,489,1340,0]C`[3974,13,3975,0,121,1340,0,\"impl-Debug-for-Dir\"]Cb[3974,13,3975,0,121,1990,0,\"impl-Display-for-Dir\"]Ch[3974,13,3975,0,2436,1990,0,\"impl-Display-for-StreamId\"]Cf[3974,13,3975,0,2436,1340,0,\"impl-Debug-for-StreamId\"]Al[3974,13,3975,0,2445,1340,0]Ak[3974,13,3725,0,115,1340,0]Al[3974,13,3725,0,3134,1340,0]Al[3974,13,3725,0,1337,1340,0]Al[3974,13,3725,0,4014,1340,0]Al[3974,13,3725,0,2003,1340,0]Al[3974,13,3725,0,4307,1340,0]Al[3974,13,3725,0,4795,1340,0]Al[3974,13,1855,0,5112,1340,0]Al[3974,13,1855,0,4012,1340,0]Al[3974,13,1855,0,4867,1340,0]Db[3974,13,1934,0,4960,1340,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dd[3974,13,1934,0,4960,1990,0,\"impl-Display-for-NoInitialCipherSuite\"]Cc[3974,13,1934,0,1350,1340,0,\"impl-Debug-for-Error\"]Ce[3974,13,1934,0,1350,1990,0,\"impl-Display-for-Error\"]Ao[4761,13,4762,3731,3326,1340,0]Ao[4761,13,4762,2459,2414,1340,0]Ci[4761,13,4762,1971,1614,1990,0,\"impl-Display-for-VarInt\"]Cg[4761,13,4762,1971,1614,1340,0,\"impl-Debug-for-VarInt\"]De[4761,13,4762,1971,4965,1340,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Dg[4761,13,4762,1971,4965,1990,0,\"impl-Display-for-VarIntBoundsExceeded\"]Ao[4761,13,4762,3181,1334,1340,0]Dd[4761,13,4762,3190,4806,1990,0,\"impl-Display-for-SendDatagramError\"]Db[4761,13,4762,3190,4806,1340,0,\"impl-Debug-for-SendDatagramError\"]Ci[4761,13,4762,1537,1597,1990,0,\"impl-Display-for-PathId\"]Cg[4761,13,4762,1537,1597,1340,0,\"impl-Debug-for-PathId\"]Ao[4761,13,4762,1537,4185,1340,0]Ao[4761,13,4762,1537,3346,1340,0]Ao[4761,13,4762,1537,3161,1340,0]Dc[4761,13,4762,1537,4860,1340,0,\"impl-Debug-for-SetPathStatusError\"]De[4761,13,4762,1537,4860,1990,0,\"impl-Display-for-SetPathStatusError\"]Ck[4761,13,4762,1537,3321,1340,0,\"impl-Debug-for-ClosedPath\"]Cm[4761,13,4762,1537,3321,1990,0,\"impl-Display-for-ClosedPath\"]Ao[4761,13,4762,1552,2449,1340,0]Ao[4761,13,4762,1552,3332,1340,0]Ao[4761,13,4762,1552,3163,1340,0]Ao[4761,13,4762,1552,4524,1340,0]Cl[4761,13,4762,1083,3166,1990,0,\"impl-Display-for-ReadError\"]Cj[4761,13,4762,1083,3166,1340,0,\"impl-Debug-for-ReadError\"]D`[4761,13,4762,1083,4315,1990,0,\"impl-Display-for-ReadableError\"]Cn[4761,13,4762,1083,4315,1340,0,\"impl-Debug-for-ReadableError\"]Ao[4761,13,4762,1088,2031,1340,0]Cm[4761,13,4762,1088,3371,1990,0,\"impl-Display-for-WriteError\"]Ck[4761,13,4762,1088,3371,1340,0,\"impl-Debug-for-WriteError\"]Cl[4761,13,4762,1088,4018,1340,0,\"impl-Debug-for-FinishError\"]Cn[4761,13,4762,1088,4018,1990,0,\"impl-Display-for-FinishError\"]Ao[4761,13,4762,2164,4036,1340,0]Ao[4761,13,4762,2164,4425,1340,0]Co[4761,13,4762,2164,4151,1990,0,\"impl-Display-for-ClosedStream\"]Cm[4761,13,4762,2164,4151,1340,0,\"impl-Debug-for-ClosedStream\"]D`[4761,13,4762,3731,4520,1340,0,\"impl-Debug-for-ConnectionError\"]Db[4761,13,4762,3731,4520,1990,0,\"impl-Display-for-ConnectionError\"]Cl[4761,13,4762,3731,3158,1990,0,\"impl-Display-for-PathError\"]Cj[4761,13,4762,3731,3158,1340,0,\"impl-Debug-for-PathError\"]Da[4761,13,4762,3731,4391,1990,0,\"impl-Display-for-ClosePathError\"]Co[4761,13,4762,3731,4391,1340,0,\"impl-Debug-for-ClosePathError\"]Di[4761,13,4762,3731,5050,1990,0,\"impl-Display-for-MultipathNotNegotiated\"]Dg[4761,13,4762,3731,5050,1340,0,\"impl-Debug-for-MultipathNotNegotiated\"]Ao[4761,13,4762,3731,1355,1340,0]Ao[4761,13,4762,1079,4534,1340,0]Ao[4761,13,4762,3308,4541,1340,0]Ao[4761,13,4762,3308,4830,1340,0]Ao[4761,13,4762,3308,4845,1340,0]Ao[4761,13,4762,3308,4020,1340,0]Ao[4761,13,4762,1853,4401,1340,0]Ao[4761,13,4762,1853,4191,1340,0]Ao[4761,13,4762,1853,5022,1340,0]Ao[4761,13,4762,1853,4143,1340,0]Cn[4761,13,4762,1853,4010,1990,0,\"impl-Display-for-ConfigError\"]Cl[4761,13,4762,1853,4010,1340,0,\"impl-Debug-for-ConfigError\"]Cl[4761,13,4762,1527,3146,1990,0,\"impl-Display-for-FrameType\"]Cj[4761,13,4762,1527,3146,1340,0,\"impl-Debug-for-FrameType\"]Da[4761,13,4762,1527,4406,1990,0,\"impl-Display-for-InvalidFrameId\"]Co[4761,13,4762,1527,4406,1340,0,\"impl-Debug-for-InvalidFrameId\"]Cm[4761,13,4762,1527,3338,1990,0,\"impl-Display-for-MaybeFrame\"]Ck[4761,13,4762,1527,3338,1340,0,\"impl-Debug-for-MaybeFrame\"]Ck[4761,13,4762,1527,3360,1340,0,\"impl-Debug-for-StreamInfo\"]Cm[4761,13,4762,1527,3360,1990,0,\"impl-Display-for-StreamInfo\"]Co[4761,13,4762,1527,4158,1990,0,\"impl-Display-for-DatagramInfo\"]Cm[4761,13,4762,1527,4158,1340,0,\"impl-Debug-for-DatagramInfo\"]Db[4761,13,4762,1527,4518,1990,0,\"impl-Display-for-ConnectionClose\"]D`[4761,13,4762,1527,4518,1340,0,\"impl-Debug-for-ConnectionClose\"]Da[4761,13,4762,1527,4708,1340,0,\"impl-Debug-for-ApplicationClose\"]Dc[4761,13,4762,1527,4708,1990,0,\"impl-Display-for-ApplicationClose\"]Ci[4761,13,4762,1527,2407,1340,0,\"impl-Debug-for-Datagram\"]Ck[4761,13,4762,1527,2407,1990,0,\"impl-Display-for-Datagram\"]Ao[4761,13,4762,2459,4712,1340,0]Ao[4761,13,4762,2459,2418,1340,0]Co[4761,13,4762,2459,4154,1990,0,\"impl-Display-for-ConnectError\"]Cm[4761,13,4762,2459,4154,1340,0,\"impl-Debug-for-ConnectError\"]Ao[4761,13,4762,2459,4004,1340,0]Cm[4761,13,4762,2459,3354,1990,0,\"impl-Display-for-RetryError\"]Ck[4761,13,4762,2459,3354,1340,0,\"impl-Debug-for-RetryError\"]Ao[4761,13,4762,1918,4310,1340,0]Ao[4761,13,4762,1918,4532,1340,0]Ao[4761,13,4762,1918,5053,1340,0]Ao[4761,13,4762,1918,2420,1340,0]Db[4761,13,4762,1918,4804,1340,0,\"impl-Debug-for-PacketDecodeError\"]Dd[4761,13,4762,1918,4804,1990,0,\"impl-Display-for-PacketDecodeError\"]Ao[4761,13,4762,1945,4522,1340,0]Ao[4761,13,4762,1945,4287,1340,0]Cm[4761,13,4762,1945,4157,1340,0,\"impl-Debug-for-ConnectionId\"]Co[4761,13,4762,1945,4157,1990,0,\"impl-Display-for-ConnectionId\"]Ao[4761,13,4762,1945,4162,1340,0]Ce[4761,13,4762,0,4429,1990,0,\"impl-Display-for-Error\"]Cc[4761,13,4762,0,4429,1340,0,\"impl-Debug-for-Error\"]Cb[4761,13,4762,0,4864,1340,0,\"impl-Debug-for-Code\"]Cd[4761,13,4762,0,4864,1990,0,\"impl-Display-for-Code\"]Ao[4761,13,4762,4328,3335,1340,0]Ao[4761,13,4762,4328,5146,1340,0]Ao[4761,13,4762,4895,4738,1340,0]An[4761,13,4762,4762,490,1340,0]Ce[4761,13,4762,4762,122,1990,0,\"impl-Display-for-Dir\"]Cc[4761,13,4762,4762,122,1340,0,\"impl-Debug-for-Dir\"]Ck[4761,13,4762,4762,2437,1990,0,\"impl-Display-for-StreamId\"]Ci[4761,13,4762,4762,2437,1340,0,\"impl-Debug-for-StreamId\"]Ao[4761,13,4762,4762,2446,1340,0]Cl[4761,13,4762,4762,3144,1990,0,\"impl-Display-for-FourTuple\"]Cj[4761,13,4762,4762,3144,1340,0,\"impl-Debug-for-FourTuple\"]Cn[4761,13,1851,1851,4321,1340,0,\"impl-Debug-for-UnexpectedEnd\"]D`[4761,13,1851,1851,4321,1990,0,\"impl-Display-for-UnexpectedEnd\"]Ao[4761,13,5006,5006,4905,1340,0]Cf[4761,13,5006,5006,1352,1340,0,\"impl-Debug-for-Error\"]Ch[4761,13,5006,5006,1352,1990,0,\"impl-Display-for-Error\"]Ao[4761,13,1856,1856,5113,1340,0]Ao[4761,13,1856,1856,4013,1340,0]Ao[4761,13,1856,1856,4870,1340,0]De[4761,13,1935,1935,4961,1340,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dg[4761,13,1935,1935,4961,1990,0,\"impl-Display-for-NoInitialCipherSuite\"]Ce[4761,13,1935,0,1351,1990,0,\"impl-Display-for-Error\"]Cc[4761,13,1935,0,1351,1340,0,\"impl-Debug-for-Error\"]Am[4761,13,3726,137,116,1340,0]An[4761,13,3726,137,3135,1340,0]Ao[4761,13,3726,1522,1338,1340,0]Ao[4761,13,3726,1522,4015,1340,0]Ao[4761,13,3726,2507,2004,1340,0]Ao[4761,13,3726,2507,4308,1340,0]Ao[4761,13,3726,3726,4796,1340,0]Cf[4761,13,2135,2135,1353,1340,0,\"impl-Debug-for-Error\"]Ch[4761,13,2135,2135,1353,1990,0,\"impl-Display-for-Error\"]Ao[4761,13,2135,2135,1356,1340,0]An[4465,13,4466,397,4430,1340,0]Ao[4465,13,4466,4466,2434,1340,0]Ao[4465,13,4466,4466,2447,1340,0]Ao[4465,13,4466,4466,4163,1340,0]Al[4761,13,4762,1537,4185,0,0]Ai[3974,14,3975,0,2448,0,0]Al[4761,14,4762,1552,2449,0,0]Al[4761,13,4762,1918,4310,0,0]Al[4761,14,4762,1918,5053,0,0]Ah[4761,14,4531,0,478,0,0]Al[4465,14,4466,4466,2434,0,0]Ai[3974,13,3975,0,5021,0,0]Al[4761,13,4762,1853,5022,0,0]8Al[1374,13,1551,1551,4426,0,0]Al[4043,13,4044,4044,5054,0,0]Ai[3974,13,3975,0,4279,0,0]Al[3974,13,3975,2458,2413,0,0]Ai[3974,13,3975,0,3347,0,0]Ai[3974,13,3975,0,4533,0,0]Ai[3974,13,3975,0,4400,0,0]Ai[3974,13,3975,0,4189,0,0]Ai[3974,13,3975,0,4141,0,0]Ai[3974,13,3975,0,4156,0,0]Ai[3974,13,3975,0,4737,0,0]Ai[3974,13,3975,0,2436,0,0]Ah[3974,13,3725,0,115,0,0]Ai[3974,13,3725,0,1337,0,0]Ai[3974,13,3725,0,2003,0,0]Al[4761,13,4762,4550,4280,0,0]Al[4761,13,4762,1079,3348,0,0]Al[4761,13,4762,2459,2414,0,0]Al[4761,13,4762,1918,5171,0,0]Al[4761,13,4762,4328,5141,0,0]Al[4761,13,4762,1079,4534,0,0]Al[4761,13,4762,1853,4401,0,0]Al[4761,13,4762,1853,4191,0,0]Al[4761,13,4762,1853,4143,0,0]Al[4761,13,4762,1918,4310,0,0]Al[4761,13,4762,1945,4157,0,0]Ai[4761,13,4762,0,4429,0,0]Al[4761,13,4762,4328,5146,0,0]Al[4761,13,4762,4895,4738,0,0]Al[4761,13,4762,4762,2437,0,0]Aj[4761,13,3726,137,116,0,0]Al[4761,13,3726,1522,1338,0,0]Al[4761,13,3726,2507,2004,0,0]Ak[4465,13,4466,397,4430,0,0]Al[1058,13,4248,4248,5098,0,0]Al[1058,13,4248,4248,5099,0,0]Aj[3974,13,3975,0,489,128,0]Am[4761,13,4762,4762,490,128,0]Ai[3974,12,3975,0,3363,0,0]Al[3974,13,3975,2156,2015,0,0]0Ao[3974,13,3975,1564,4196,2015,0]Al[3974,13,3975,0,4319,3363,0]Al[4761,12,4762,1853,3364,0,0]Ao[4761,13,4762,1853,4320,3364,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,13,4762,3731,3326,0,0]Al[4761,14,4762,1552,3163,0,0]Ae[1058,7,1845,0,0,0,0]Ae[1058,7,1940,0,0,0,0]Ae[3974,4,3975,0,0,0,0]Ai[3974,15,3975,0,4161,0,0]Al[4761,15,4762,1945,4162,0,0]Al[4465,15,4466,4466,4163,0,0]210Ah[3974,5,1855,1856,0,0,0]Ae[4761,5,1856,0,0,0,0]Al[4761,15,4762,1918,4532,0,0]Al[4761,15,4762,1527,3338,0,0]Ah[3974,5,3975,1057,0,0,0]Al[4761,15,4762,3731,1355,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Al[3974,15,3975,4110,4424,0,0]Ah[3974,6,3975,4762,0,0,0]Ae[4761,6,4762,0,0,0,0]Ai[3974,19,3975,0,1596,0,0]Al[4761,19,4762,1537,1597,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]Ai[4761,14,3159,0,4175,0,0]Al[4465,14,4466,4466,2434,0,0]Aa[499,3,0,0,0,0,0]Ai[4761,14,4762,0,4429,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1552,3163,0,0]Al[4761,14,4762,1527,2407,0,0]An[3974,13,3975,3730,2427,466,0]An[3974,13,3975,2485,2417,466,0]Am[3974,13,3975,1057,482,466,0]An[3974,13,3975,1057,4431,466,0]An[3974,13,3975,4110,3350,466,0]An[3974,13,3975,4115,3356,466,0]An[4761,13,4762,1083,1578,466,0]Am[1374,13,1375,1375,129,473,0]An[1374,13,1375,1375,4006,473,0]An[1374,13,1551,1551,4037,473,0]An[1374,13,1551,1551,1370,473,0]An[1374,13,1551,1551,4426,473,0]Ak[499,13,500,500,4007,473,0]An[4043,13,4044,4044,5054,473,0]An[3974,13,3975,3730,4545,473,0]An[3974,13,3975,3730,2427,473,0]Ak[3974,13,3975,0,4279,473,0]An[3974,13,3975,1057,2428,473,0]An[3974,13,3975,1057,4704,473,0]An[3974,13,3975,3730,3323,473,0]An[3974,13,3975,3730,3325,473,0]An[3974,13,3975,3730,2006,473,0]An[3974,13,3975,3730,1589,473,0]An[3974,13,3975,3730,3127,473,0]An[3974,13,3975,3730,2401,473,0]An[3974,13,3975,3730,4181,473,0]An[3974,13,3975,3730,4186,473,0]An[3974,13,3975,3730,4967,473,0]An[3974,13,3975,3730,4805,473,0]0An[3974,13,3975,2458,2413,473,0]An[3974,13,3975,2458,4288,473,0]An[3974,13,3975,2458,1570,473,0]An[3974,13,3975,2485,2417,473,0]An[3974,13,3975,2485,3353,473,0]An[3974,13,3975,2485,4405,473,0]Am[3974,13,3975,1057,482,473,0]An[3974,13,3975,1057,4431,473,0]An[3974,13,3975,4110,3350,473,0]An[3974,13,3975,4110,4906,473,0]An[3974,13,3975,4110,4424,473,0]0An[3974,13,3975,4110,3165,473,0]Dh[3974,13,3975,4110,3165,473,0,\"impl-From%3CResetError%3E-for-ReadError\"]Dm[3974,13,3975,4110,3165,473,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]An[3974,13,3975,4110,3352,473,0]0An[3974,13,3975,4110,4423,473,0]0An[3974,13,3975,1564,4196,473,0]An[3974,13,3975,1100,4032,473,0]An[3974,13,3975,4115,3356,473,0]Dk[3974,13,3975,4115,3370,473,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dn[3974,13,3975,4115,3370,473,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Dk[3974,13,3975,4115,3370,473,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]An[3974,13,3975,4115,3370,473,0]An[3974,13,3975,4115,4195,473,0]0An[3974,13,3975,4115,2020,473,0]Ak[3974,13,3975,0,3347,473,0]Ak[3974,13,3975,0,4319,473,0]Ak[3974,13,3975,0,4537,473,0]Ak[3974,13,3975,0,4173,473,0]Ak[3974,13,3975,0,4414,473,0]Cj[3974,13,3975,0,1613,473,0,\"impl-From%3Cu8%3E-for-VarInt\"]D`[3974,13,3975,0,1613,473,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Dj[3974,13,3975,0,1613,473,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Ak[3974,13,3975,0,1613,473,0]Ck[3974,13,3975,0,1613,473,0,\"impl-From%3Cu16%3E-for-VarInt\"]Ck[3974,13,3975,0,1613,473,0,\"impl-From%3Cu32%3E-for-VarInt\"]Ak[3974,13,3975,0,4964,473,0]Ak[3974,13,3975,0,1333,473,0]Ak[3974,13,3975,0,1596,473,0]0Ak[3974,13,3975,0,2448,473,0]Ak[3974,13,3975,0,3331,473,0]Ak[3974,13,3975,0,3162,473,0]Ak[3974,13,3975,0,4523,473,0]Ak[3974,13,3975,0,2030,473,0]Ak[3974,13,3975,0,4147,473,0]Ak[3974,13,3975,0,4519,473,0]Df[3974,13,3975,0,4519,473,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Dl[3974,13,3975,0,4519,473,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Do[3974,13,3975,0,4519,473,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ak[3974,13,3975,0,4533,473,0]Ak[3974,13,3975,0,4540,473,0]Ak[3974,13,3975,0,4829,473,0]Ak[3974,13,3975,0,4844,473,0]Ak[3974,13,3975,0,4019,473,0]0Ak[3974,13,3975,0,4400,473,0]Ak[3974,13,3975,0,4189,473,0]Ak[3974,13,3975,0,5021,473,0]Ak[3974,13,3975,0,4141,473,0]Ea[3974,13,3975,0,4009,473,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ak[3974,13,3975,0,4009,473,0]Dl[3974,13,3975,0,4009,473,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ak[3974,13,3975,0,3145,473,0]Ak[3974,13,3975,0,4516,473,0]0Ak[3974,13,3975,0,4706,473,0]Ak[3974,13,3975,0,4153,473,0]0Ak[3974,13,3975,0,4156,473,0]Ak[3974,13,3975,0,4161,473,0]Ak[3974,13,3975,0,4863,473,0]0Ak[3974,13,3975,0,3334,473,0]Ak[3974,13,3975,0,4737,473,0]Aj[3974,13,3975,0,489,473,0]Aj[3974,13,3975,0,121,473,0]Ak[3974,13,3975,0,2436,473,0]0Ak[3974,13,3975,0,2445,473,0]Aj[3974,13,3725,0,115,473,0]Ak[3974,13,3725,0,3134,473,0]Ak[3974,13,3725,0,1337,473,0]Ak[3974,13,3725,0,4014,473,0]Ak[3974,13,3725,0,2003,473,0]Ak[3974,13,3725,0,4307,473,0]Ak[3974,13,3725,0,4795,473,0]Ak[3974,13,1855,0,1998,473,0]Aj[3974,13,1855,0,476,473,0]Ak[3974,13,1855,0,5112,473,0]Ak[3974,13,1855,0,4012,473,0]0Ak[3974,13,1855,0,4867,473,0]Ak[3974,13,1934,0,3365,473,0]Ak[3974,13,1934,0,4290,473,0]Ak[3974,13,1934,0,4733,473,0]Ak[3974,13,1934,0,4735,473,0]Ak[3974,13,1934,0,4960,473,0]Df[3974,13,1934,0,1350,473,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]E`[3974,13,1934,0,1350,473,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]E`[3974,13,1934,0,1350,473,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Da[3974,13,1934,0,1350,473,0,\"impl-From%3COtherError%3E-for-Error\"]De[3974,13,1934,0,1350,473,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dg[3974,13,1934,0,1350,473,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]De[3974,13,1934,0,1350,473,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dg[3974,13,1934,0,1350,473,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dg[3974,13,1934,0,1350,473,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Df[3974,13,1934,0,1350,473,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Ak[3974,13,1934,0,1350,473,0]Dn[3974,13,1934,0,1350,473,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]An[4761,13,4762,4550,4280,473,0]An[4761,13,4762,3190,3140,473,0]An[4761,13,4762,1083,1578,473,0]An[4761,13,4762,2164,2024,473,0]An[4761,13,4762,2164,3351,473,0]An[4761,13,4762,2164,3357,473,0]An[4761,13,4762,3731,3326,473,0]An[4761,13,4762,1079,3348,473,0]An[4761,13,4762,1853,4320,473,0]An[4761,13,4762,2459,2414,473,0]An[4761,13,4762,2459,4286,473,0]An[4761,13,4762,1918,5171,473,0]An[4761,13,4762,4328,5141,473,0]An[4761,13,4762,1563,4538,473,0]An[4761,13,4762,1563,4174,473,0]An[4761,13,4762,1563,4415,473,0]Cm[4761,13,4762,1971,1614,473,0,\"impl-From%3Cu8%3E-for-VarInt\"]Cn[4761,13,4762,1971,1614,473,0,\"impl-From%3Cu32%3E-for-VarInt\"]Co[4761,13,4762,1971,1614,473,0,\"impl-From%3CCode%3E-for-VarInt\"]Cn[4761,13,4762,1971,1614,473,0,\"impl-From%3Cu16%3E-for-VarInt\"]An[4761,13,4762,1971,1614,473,0]Dc[4761,13,4762,1971,1614,473,0,\"impl-From%3CStreamId%3E-for-VarInt\"]An[4761,13,4762,1971,4965,473,0]An[4761,13,4762,3181,1334,473,0]An[4761,13,4762,3190,4806,473,0]An[4761,13,4762,1537,1597,473,0]0An[4761,13,4762,1537,4185,473,0]An[4761,13,4762,1537,3346,473,0]An[4761,13,4762,1537,3161,473,0]An[4761,13,4762,1537,4860,473,0]An[4761,13,4762,1537,3321,473,0]An[4761,13,4762,1552,2449,473,0]An[4761,13,4762,1552,3332,473,0]An[4761,13,4762,1552,3163,473,0]An[4761,13,4762,1552,4524,473,0]An[4761,13,4762,1083,3166,473,0]An[4761,13,4762,1083,4315,473,0]An[4761,13,4762,1088,2031,473,0]An[4761,13,4762,1088,3371,473,0]An[4761,13,4762,1088,4018,473,0]An[4761,13,4762,2164,4036,473,0]An[4761,13,4762,2164,4425,473,0]An[4761,13,4762,2164,4151,473,0]An[4761,13,4762,3731,4520,473,0]0An[4761,13,4762,3731,3158,473,0]An[4761,13,4762,3731,4391,473,0]An[4761,13,4762,3731,5050,473,0]An[4761,13,4762,3731,1355,473,0]0An[4761,13,4762,1079,4534,473,0]An[4761,13,4762,3308,4541,473,0]An[4761,13,4762,3308,4830,473,0]An[4761,13,4762,3308,4845,473,0]An[4761,13,4762,3308,4020,473,0]0An[4761,13,4762,1853,4401,473,0]An[4761,13,4762,1853,4191,473,0]An[4761,13,4762,1853,5022,473,0]An[4761,13,4762,1853,4143,473,0]Ed[4761,13,4762,1853,4010,473,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Do[4761,13,4762,1853,4010,473,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]An[4761,13,4762,1853,4010,473,0]An[4761,13,4762,1527,3146,473,0]An[4761,13,4762,1527,4406,473,0]An[4761,13,4762,1527,3338,473,0]An[4761,13,4762,1527,3360,473,0]An[4761,13,4762,1527,4158,473,0]An[4761,13,4762,1527,4518,473,0]0An[4761,13,4762,1527,4708,473,0]An[4761,13,4762,1527,2407,473,0]An[4761,13,4762,2459,4712,473,0]An[4761,13,4762,2459,2418,473,0]An[4761,13,4762,2459,4154,473,0]0An[4761,13,4762,2459,4004,473,0]An[4761,13,4762,2459,3354,473,0]An[4761,13,4762,1918,4310,473,0]An[4761,13,4762,1918,4532,473,0]An[4761,13,4762,1918,5053,473,0]An[4761,13,4762,1918,2420,473,0]An[4761,13,4762,1918,4804,473,0]0An[4761,13,4762,1945,4522,473,0]An[4761,13,4762,1945,4287,473,0]An[4761,13,4762,1945,4157,473,0]An[4761,13,4762,1945,4162,473,0]Ak[4761,13,4762,0,4429,473,0]0Ak[4761,13,4762,0,4864,473,0]0An[4761,13,4762,4328,3335,473,0]An[4761,13,4762,4328,5146,473,0]An[4761,13,4762,4895,4738,473,0]Am[4761,13,4762,4762,490,473,0]Am[4761,13,4762,4762,122,473,0]An[4761,13,4762,4762,2437,473,0]0An[4761,13,4762,4762,2446,473,0]An[4761,13,4762,4762,3144,473,0]An[4761,13,1851,1851,4321,473,0]An[4761,13,5006,5006,4905,473,0]An[4761,13,5006,5006,1352,473,0]0An[4761,13,1856,1856,1999,473,0]Am[4761,13,1856,1856,477,473,0]An[4761,13,1856,1856,5113,473,0]An[4761,13,1856,1856,4013,473,0]0An[4761,13,1856,1856,4870,473,0]An[4761,13,1935,1935,3366,473,0]An[4761,13,1935,1935,4291,473,0]An[4761,13,1935,1935,4734,473,0]An[4761,13,1935,1935,4736,473,0]An[4761,13,1935,1935,4961,473,0]Dg[4761,13,1935,0,1351,473,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]E`[4761,13,1935,0,1351,473,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Dg[4761,13,1935,0,1351,473,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Dn[4761,13,1935,0,1351,473,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]De[4761,13,1935,0,1351,473,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[4761,13,1935,0,1351,473,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]E`[4761,13,1935,0,1351,473,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dg[4761,13,1935,0,1351,473,0,\"impl-From%3CCertificateError%3E-for-Error\"]Ak[4761,13,1935,0,1351,473,0]Df[4761,13,1935,0,1351,473,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[4761,13,1935,0,1351,473,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Da[4761,13,1935,0,1351,473,0,\"impl-From%3COtherError%3E-for-Error\"]Al[4761,13,3726,137,116,473,0]Am[4761,13,3726,137,3135,473,0]An[4761,13,3726,1522,1338,473,0]An[4761,13,3726,1522,4015,473,0]An[4761,13,3726,2507,2004,473,0]An[4761,13,3726,2507,4308,473,0]An[4761,13,3726,3726,4796,473,0]An[4761,13,2135,2135,1353,473,0]An[4761,13,2135,2135,1356,473,0]An[4465,13,4466,4466,3369,473,0]0Am[4465,13,4466,397,4430,473,0]An[4465,13,4466,4466,2434,473,0]An[4465,13,4466,4466,2447,473,0]An[4465,13,4466,4466,4163,473,0]An[1058,13,1059,1059,3137,473,0]An[1058,13,1059,1059,4899,473,0]Am[1058,13,1845,1845,130,473,0]An[1058,13,1553,1553,1371,473,0]An[1058,13,1553,1553,4038,473,0]An[1058,13,1553,1553,4528,473,0]An[1058,13,4248,4248,5098,473,0]An[1058,13,4248,4248,5099,473,0]Am[1058,13,1940,1940,131,473,0]Am[3994,13,3995,3995,120,473,0]An[3994,13,3995,3995,2404,473,0]Ak[3974,13,3975,0,1613,474,0]Ak[3974,13,3975,0,1596,474,0]Ak[3974,13,3975,0,4019,474,0]Ak[3974,13,3975,0,4156,474,0]Aj[3974,13,3975,0,489,474,0]Aj[3974,13,3975,0,121,474,0]Ak[3974,13,3975,0,2436,474,0]An[4761,13,4762,1971,1614,474,0]An[4761,13,4762,3190,4806,474,0]An[4761,13,4762,1537,1597,474,0]An[4761,13,4762,1083,3166,474,0]An[4761,13,4762,1083,4315,474,0]An[4761,13,4762,1088,3371,474,0]An[4761,13,4762,3308,4020,474,0]An[4761,13,4762,2459,4712,474,0]An[4761,13,4762,1918,4804,474,0]An[4761,13,4762,1945,4157,474,0]Am[4761,13,4762,4762,490,474,0]Am[4761,13,4762,4762,122,474,0]An[4761,13,4762,4762,2437,474,0]An[4761,13,4762,4762,3144,474,0]Am[1374,13,1375,1375,129,475,0]An[1374,13,1375,1375,4006,475,0]An[1374,13,1551,1551,4037,475,0]An[1374,13,1551,1551,1370,475,0]An[1374,13,1551,1551,4426,475,0]Ak[499,13,500,500,4007,475,0]An[4043,13,4044,4044,5054,475,0]An[3974,13,3975,3730,4545,475,0]An[3974,13,3975,3730,2427,475,0]Ak[3974,13,3975,0,4279,475,0]An[3974,13,3975,1057,2428,475,0]An[3974,13,3975,1057,4704,475,0]An[3974,13,3975,3730,3323,475,0]An[3974,13,3975,3730,3325,475,0]An[3974,13,3975,3730,2006,475,0]An[3974,13,3975,3730,1589,475,0]An[3974,13,3975,3730,3127,475,0]An[3974,13,3975,3730,2401,475,0]An[3974,13,3975,3730,4181,475,0]An[3974,13,3975,3730,4186,475,0]An[3974,13,3975,3730,4967,475,0]An[3974,13,3975,3730,4805,475,0]An[3974,13,3975,2458,2413,475,0]An[3974,13,3975,2458,4288,475,0]An[3974,13,3975,2458,1570,475,0]An[3974,13,3975,2485,2417,475,0]An[3974,13,3975,2485,3353,475,0]An[3974,13,3975,2485,4405,475,0]Am[3974,13,3975,1057,482,475,0]An[3974,13,3975,1057,4431,475,0]An[3974,13,3975,4110,3350,475,0]An[3974,13,3975,4110,4906,475,0]An[3974,13,3975,4110,4424,475,0]An[3974,13,3975,4110,3165,475,0]An[3974,13,3975,4110,3352,475,0]An[3974,13,3975,4110,4423,475,0]An[3974,13,3975,1564,4196,475,0]An[3974,13,3975,1100,4032,475,0]An[3974,13,3975,4115,3356,475,0]An[3974,13,3975,4115,3370,475,0]An[3974,13,3975,4115,4195,475,0]An[3974,13,3975,4115,2020,475,0]Ak[3974,13,3975,0,3347,475,0]Ak[3974,13,3975,0,4319,475,0]Ak[3974,13,3975,0,4537,475,0]Ak[3974,13,3975,0,4173,475,0]Ak[3974,13,3975,0,4414,475,0]Ak[3974,13,3975,0,1613,475,0]Ak[3974,13,3975,0,4964,475,0]Ak[3974,13,3975,0,1333,475,0]Ak[3974,13,3975,0,1596,475,0]Ak[3974,13,3975,0,2448,475,0]Ak[3974,13,3975,0,3331,475,0]Ak[3974,13,3975,0,3162,475,0]Ak[3974,13,3975,0,4523,475,0]Ak[3974,13,3975,0,2030,475,0]Ak[3974,13,3975,0,4147,475,0]Ak[3974,13,3975,0,4519,475,0]Ak[3974,13,3975,0,4533,475,0]Ak[3974,13,3975,0,4540,475,0]Ak[3974,13,3975,0,4829,475,0]Ak[3974,13,3975,0,4844,475,0]Ak[3974,13,3975,0,4019,475,0]Ak[3974,13,3975,0,4400,475,0]Ak[3974,13,3975,0,4189,475,0]Ak[3974,13,3975,0,5021,475,0]Ak[3974,13,3975,0,4141,475,0]Ak[3974,13,3975,0,4009,475,0]Ak[3974,13,3975,0,3145,475,0]Ak[3974,13,3975,0,4516,475,0]Ak[3974,13,3975,0,4706,475,0]Ak[3974,13,3975,0,4153,475,0]Ak[3974,13,3975,0,4156,475,0]Ak[3974,13,3975,0,4161,475,0]Ak[3974,13,3975,0,4863,475,0]Ak[3974,13,3975,0,3334,475,0]Ak[3974,13,3975,0,4737,475,0]Aj[3974,13,3975,0,489,475,0]Aj[3974,13,3975,0,121,475,0]Ak[3974,13,3975,0,2436,475,0]Ak[3974,13,3975,0,2445,475,0]Aj[3974,13,3725,0,115,475,0]Ak[3974,13,3725,0,3134,475,0]Ak[3974,13,3725,0,1337,475,0]Ak[3974,13,3725,0,4014,475,0]Ak[3974,13,3725,0,2003,475,0]Ak[3974,13,3725,0,4307,475,0]Ak[3974,13,3725,0,4795,475,0]Ak[3974,13,1855,0,1998,475,0]Aj[3974,13,1855,0,476,475,0]Ak[3974,13,1855,0,5112,475,0]Ak[3974,13,1855,0,4012,475,0]Ak[3974,13,1855,0,4867,475,0]Ak[3974,13,1934,0,3365,475,0]Ak[3974,13,1934,0,4290,475,0]Ak[3974,13,1934,0,4733,475,0]Ak[3974,13,1934,0,4735,475,0]Ak[3974,13,1934,0,4960,475,0]Ak[3974,13,1934,0,1350,475,0]An[4761,13,4762,4550,4280,475,0]An[4761,13,4762,3190,3140,475,0]An[4761,13,4762,1083,1578,475,0]An[4761,13,4762,2164,2024,475,0]An[4761,13,4762,2164,3351,475,0]An[4761,13,4762,2164,3357,475,0]An[4761,13,4762,3731,3326,475,0]An[4761,13,4762,1079,3348,475,0]An[4761,13,4762,1853,4320,475,0]An[4761,13,4762,2459,2414,475,0]An[4761,13,4762,2459,4286,475,0]An[4761,13,4762,1918,5171,475,0]An[4761,13,4762,4328,5141,475,0]An[4761,13,4762,1563,4538,475,0]An[4761,13,4762,1563,4174,475,0]An[4761,13,4762,1563,4415,475,0]An[4761,13,4762,1971,1614,475,0]An[4761,13,4762,1971,4965,475,0]An[4761,13,4762,3181,1334,475,0]An[4761,13,4762,3190,4806,475,0]An[4761,13,4762,1537,1597,475,0]An[4761,13,4762,1537,4185,475,0]An[4761,13,4762,1537,3346,475,0]An[4761,13,4762,1537,3161,475,0]An[4761,13,4762,1537,4860,475,0]An[4761,13,4762,1537,3321,475,0]An[4761,13,4762,1552,2449,475,0]An[4761,13,4762,1552,3332,475,0]An[4761,13,4762,1552,3163,475,0]An[4761,13,4762,1552,4524,475,0]An[4761,13,4762,1083,3166,475,0]An[4761,13,4762,1083,4315,475,0]An[4761,13,4762,1088,2031,475,0]An[4761,13,4762,1088,3371,475,0]An[4761,13,4762,1088,4018,475,0]An[4761,13,4762,2164,4036,475,0]An[4761,13,4762,2164,4425,475,0]An[4761,13,4762,2164,4151,475,0]An[4761,13,4762,3731,4520,475,0]An[4761,13,4762,3731,3158,475,0]An[4761,13,4762,3731,4391,475,0]An[4761,13,4762,3731,5050,475,0]An[4761,13,4762,3731,1355,475,0]An[4761,13,4762,1079,4534,475,0]An[4761,13,4762,3308,4541,475,0]An[4761,13,4762,3308,4830,475,0]An[4761,13,4762,3308,4845,475,0]An[4761,13,4762,3308,4020,475,0]An[4761,13,4762,1853,4401,475,0]An[4761,13,4762,1853,4191,475,0]An[4761,13,4762,1853,5022,475,0]An[4761,13,4762,1853,4143,475,0]An[4761,13,4762,1853,4010,475,0]An[4761,13,4762,1527,3146,475,0]An[4761,13,4762,1527,4406,475,0]An[4761,13,4762,1527,3338,475,0]An[4761,13,4762,1527,3360,475,0]An[4761,13,4762,1527,4158,475,0]An[4761,13,4762,1527,4518,475,0]An[4761,13,4762,1527,4708,475,0]An[4761,13,4762,1527,2407,475,0]An[4761,13,4762,2459,4712,475,0]An[4761,13,4762,2459,2418,475,0]An[4761,13,4762,2459,4154,475,0]An[4761,13,4762,2459,4004,475,0]An[4761,13,4762,2459,3354,475,0]An[4761,13,4762,1918,4310,475,0]An[4761,13,4762,1918,4532,475,0]An[4761,13,4762,1918,5053,475,0]An[4761,13,4762,1918,2420,475,0]An[4761,13,4762,1918,4804,475,0]An[4761,13,4762,1945,4522,475,0]An[4761,13,4762,1945,4287,475,0]An[4761,13,4762,1945,4157,475,0]An[4761,13,4762,1945,4162,475,0]Ak[4761,13,4762,0,4429,475,0]Ak[4761,13,4762,0,4864,475,0]An[4761,13,4762,4328,3335,475,0]An[4761,13,4762,4328,5146,475,0]An[4761,13,4762,4895,4738,475,0]Am[4761,13,4762,4762,490,475,0]Am[4761,13,4762,4762,122,475,0]An[4761,13,4762,4762,2437,475,0]An[4761,13,4762,4762,2446,475,0]An[4761,13,4762,4762,3144,475,0]An[4761,13,1851,1851,4321,475,0]An[4761,13,5006,5006,4905,475,0]An[4761,13,5006,5006,1352,475,0]An[4761,13,1856,1856,1999,475,0]Am[4761,13,1856,1856,477,475,0]An[4761,13,1856,1856,5113,475,0]An[4761,13,1856,1856,4013,475,0]An[4761,13,1856,1856,4870,475,0]An[4761,13,1935,1935,3366,475,0]An[4761,13,1935,1935,4291,475,0]An[4761,13,1935,1935,4734,475,0]An[4761,13,1935,1935,4736,475,0]An[4761,13,1935,1935,4961,475,0]Ak[4761,13,1935,0,1351,475,0]Al[4761,13,3726,137,116,475,0]Am[4761,13,3726,137,3135,475,0]An[4761,13,3726,1522,1338,475,0]An[4761,13,3726,1522,4015,475,0]An[4761,13,3726,2507,2004,475,0]An[4761,13,3726,2507,4308,475,0]An[4761,13,3726,3726,4796,475,0]An[4761,13,2135,2135,1353,475,0]An[4761,13,2135,2135,1356,475,0]An[4465,13,4466,4466,3369,475,0]Am[4465,13,4466,397,4430,475,0]An[4465,13,4466,4466,2434,475,0]An[4465,13,4466,4466,2447,475,0]An[4465,13,4466,4466,4163,475,0]An[1058,13,1059,1059,3137,475,0]An[1058,13,1059,1059,4899,475,0]Am[1058,13,1845,1845,130,475,0]An[1058,13,1553,1553,1371,475,0]An[1058,13,1553,1553,4038,475,0]An[1058,13,1553,1553,4528,475,0]An[1058,13,4248,4248,5098,475,0]An[1058,13,4248,4248,5099,475,0]Am[1058,13,1940,1940,131,475,0]Am[3994,13,3995,3995,120,475,0]An[3994,13,3995,3995,2404,475,0]Ac[499,7,500,0,0,0,0]Ae[4043,7,4044,0,0,0,0]Ae[4331,7,4332,0,0,0,0]Ae[3994,7,3995,0,0,0,0]Ae[4824,7,4825,0,0,0,0]Al[4761,13,4762,1083,1578,0,0]Ai[3974,12,1855,0,1982,0,0]Al[4761,13,4762,2164,2024,0,0]Al[4761,12,1856,1856,1983,0,0]Al[3974,13,3975,3730,3325,0,0]Ab[1058,3,0,0,0,0,0]Ak[3974,13,3975,1057,482,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,13,4762,3731,3326,0,0]Al[4761,14,4762,1552,3332,0,0]Al[3974,12,3975,2156,3314,0,0]Ao[3974,13,3975,3730,4545,1586,0]Ao[3974,13,3975,3730,2427,1586,0]Ao[3974,13,3975,1057,2428,1586,0]Ao[3974,13,3975,3730,3323,1586,0]Ao[3974,13,3975,3730,2006,1586,0]Ao[3974,13,3975,3730,1589,1586,0]Ao[3974,13,3975,3730,3127,1586,0]Ao[3974,13,3975,3730,2401,1586,0]Ao[3974,13,3975,3730,4181,1586,0]Ao[3974,13,3975,3730,4186,1586,0]Ao[3974,13,3975,2458,1570,1586,0]Ao[3974,13,3975,2485,4405,1586,0]Ao[3974,13,3975,4115,2020,1586,0]?Al[3974,13,3975,4110,3350,0,0]Al[4761,13,4762,2164,3351,0,0]Al[4761,13,5006,5006,4905,0,0]Al[4761,13,4762,3190,3140,0,0]Ak[4465,13,4466,397,4430,0,0]Ai[3974,12,1855,0,1982,0,0]Al[4761,12,1856,1856,1983,0,0]32Ai[3974,13,3975,0,5021,0,0]Al[4761,13,4762,1853,5022,0,0]Al[3974,13,3975,3730,3325,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,12,1855,0,1994,0,0]Al[4761,12,1856,1856,1995,0,0]Al[1374,14,1551,1551,4426,0,0]Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Ai[4761,14,4531,0,1367,0,0]Al[3974,13,3975,4110,3350,0,0]Al[3974,13,3975,4110,4906,0,0]Al[4761,13,4762,2164,3351,0,0]Ai[3974,12,3975,0,3367,0,0]Al[3974,13,3975,0,4414,3367,0]Al[3974,13,3975,0,4737,3367,0]Al[4761,12,4762,1563,3368,0,0]Ao[4761,13,4762,1563,4415,3368,0]Ao[4761,13,4762,4895,4738,3368,0]Am[1374,13,1375,1375,129,492,0]An[1374,13,1375,1375,4006,492,0]An[1374,13,1551,1551,4037,492,0]An[1374,13,1551,1551,1370,492,0]An[1374,13,1551,1551,4426,492,0]Ak[499,13,500,500,4007,492,0]An[4043,13,4044,4044,5054,492,0]An[3974,13,3975,3730,4545,492,0]An[3974,13,3975,3730,2427,492,0]Ak[3974,13,3975,0,4279,492,0]An[3974,13,3975,1057,2428,492,0]An[3974,13,3975,1057,4704,492,0]An[3974,13,3975,3730,3323,492,0]An[3974,13,3975,3730,3325,492,0]An[3974,13,3975,3730,2006,492,0]An[3974,13,3975,3730,1589,492,0]An[3974,13,3975,3730,3127,492,0]An[3974,13,3975,3730,2401,492,0]An[3974,13,3975,3730,4181,492,0]An[3974,13,3975,3730,4186,492,0]An[3974,13,3975,3730,4967,492,0]An[3974,13,3975,3730,4805,492,0]An[3974,13,3975,2458,2413,492,0]An[3974,13,3975,2458,4288,492,0]An[3974,13,3975,2458,1570,492,0]An[3974,13,3975,2485,2417,492,0]An[3974,13,3975,2485,3353,492,0]An[3974,13,3975,2485,4405,492,0]Am[3974,13,3975,1057,482,492,0]An[3974,13,3975,1057,4431,492,0]An[3974,13,3975,4110,3350,492,0]An[3974,13,3975,4110,4906,492,0]An[3974,13,3975,4110,4424,492,0]An[3974,13,3975,4110,3165,492,0]An[3974,13,3975,4110,3352,492,0]An[3974,13,3975,4110,4423,492,0]An[3974,13,3975,1564,4196,492,0]An[3974,13,3975,1100,4032,492,0]An[3974,13,3975,4115,3356,492,0]An[3974,13,3975,4115,3370,492,0]An[3974,13,3975,4115,4195,492,0]An[3974,13,3975,4115,2020,492,0]Ak[3974,13,3975,0,3347,492,0]Ak[3974,13,3975,0,4319,492,0]Ak[3974,13,3975,0,4537,492,0]Ak[3974,13,3975,0,4173,492,0]Ak[3974,13,3975,0,4414,492,0]Ak[3974,13,3975,0,1613,492,0]Ak[3974,13,3975,0,4964,492,0]Ak[3974,13,3975,0,1333,492,0]Ak[3974,13,3975,0,1596,492,0]Ak[3974,13,3975,0,2448,492,0]Ak[3974,13,3975,0,3331,492,0]Ak[3974,13,3975,0,3162,492,0]Ak[3974,13,3975,0,4523,492,0]Ak[3974,13,3975,0,2030,492,0]Ak[3974,13,3975,0,4147,492,0]Ak[3974,13,3975,0,4519,492,0]Ak[3974,13,3975,0,4533,492,0]Ak[3974,13,3975,0,4540,492,0]Ak[3974,13,3975,0,4829,492,0]Ak[3974,13,3975,0,4844,492,0]Ak[3974,13,3975,0,4019,492,0]Ak[3974,13,3975,0,4400,492,0]Ak[3974,13,3975,0,4189,492,0]Ak[3974,13,3975,0,5021,492,0]Ak[3974,13,3975,0,4141,492,0]Ak[3974,13,3975,0,4009,492,0]Ak[3974,13,3975,0,3145,492,0]Ak[3974,13,3975,0,4516,492,0]Ak[3974,13,3975,0,4706,492,0]Ak[3974,13,3975,0,4153,492,0]Ak[3974,13,3975,0,4156,492,0]Ak[3974,13,3975,0,4161,492,0]Ak[3974,13,3975,0,4863,492,0]Ak[3974,13,3975,0,3334,492,0]Ak[3974,13,3975,0,4737,492,0]Aj[3974,13,3975,0,489,492,0]Aj[3974,13,3975,0,121,492,0]Ak[3974,13,3975,0,2436,492,0]Ak[3974,13,3975,0,2445,492,0]Aj[3974,13,3725,0,115,492,0]Ak[3974,13,3725,0,3134,492,0]Ak[3974,13,3725,0,1337,492,0]Ak[3974,13,3725,0,4014,492,0]Ak[3974,13,3725,0,2003,492,0]Ak[3974,13,3725,0,4307,492,0]Ak[3974,13,3725,0,4795,492,0]Ak[3974,13,1855,0,1998,492,0]Aj[3974,13,1855,0,476,492,0]Ak[3974,13,1855,0,5112,492,0]Ak[3974,13,1855,0,4012,492,0]Ak[3974,13,1855,0,4867,492,0]Ak[3974,13,1934,0,3365,492,0]Ak[3974,13,1934,0,4290,492,0]Ak[3974,13,1934,0,4733,492,0]Ak[3974,13,1934,0,4735,492,0]Ak[3974,13,1934,0,4960,492,0]Ak[3974,13,1934,0,1350,492,0]An[4761,13,4762,4550,4280,492,0]An[4761,13,4762,3190,3140,492,0]An[4761,13,4762,1083,1578,492,0]An[4761,13,4762,2164,2024,492,0]An[4761,13,4762,2164,3351,492,0]An[4761,13,4762,2164,3357,492,0]An[4761,13,4762,3731,3326,492,0]An[4761,13,4762,1079,3348,492,0]An[4761,13,4762,1853,4320,492,0]An[4761,13,4762,2459,2414,492,0]An[4761,13,4762,2459,4286,492,0]An[4761,13,4762,1918,5171,492,0]An[4761,13,4762,4328,5141,492,0]An[4761,13,4762,1563,4538,492,0]An[4761,13,4762,1563,4174,492,0]An[4761,13,4762,1563,4415,492,0]An[4761,13,4762,1971,1614,492,0]An[4761,13,4762,1971,4965,492,0]An[4761,13,4762,3181,1334,492,0]An[4761,13,4762,3190,4806,492,0]An[4761,13,4762,1537,1597,492,0]An[4761,13,4762,1537,4185,492,0]An[4761,13,4762,1537,3346,492,0]An[4761,13,4762,1537,3161,492,0]An[4761,13,4762,1537,4860,492,0]An[4761,13,4762,1537,3321,492,0]An[4761,13,4762,1552,2449,492,0]An[4761,13,4762,1552,3332,492,0]An[4761,13,4762,1552,3163,492,0]An[4761,13,4762,1552,4524,492,0]An[4761,13,4762,1083,3166,492,0]An[4761,13,4762,1083,4315,492,0]An[4761,13,4762,1088,2031,492,0]An[4761,13,4762,1088,3371,492,0]An[4761,13,4762,1088,4018,492,0]An[4761,13,4762,2164,4036,492,0]An[4761,13,4762,2164,4425,492,0]An[4761,13,4762,2164,4151,492,0]An[4761,13,4762,3731,4520,492,0]An[4761,13,4762,3731,3158,492,0]An[4761,13,4762,3731,4391,492,0]An[4761,13,4762,3731,5050,492,0]An[4761,13,4762,3731,1355,492,0]An[4761,13,4762,1079,4534,492,0]An[4761,13,4762,3308,4541,492,0]An[4761,13,4762,3308,4830,492,0]An[4761,13,4762,3308,4845,492,0]An[4761,13,4762,3308,4020,492,0]An[4761,13,4762,1853,4401,492,0]An[4761,13,4762,1853,4191,492,0]An[4761,13,4762,1853,5022,492,0]An[4761,13,4762,1853,4143,492,0]An[4761,13,4762,1853,4010,492,0]An[4761,13,4762,1527,3146,492,0]An[4761,13,4762,1527,4406,492,0]An[4761,13,4762,1527,3338,492,0]An[4761,13,4762,1527,3360,492,0]An[4761,13,4762,1527,4158,492,0]An[4761,13,4762,1527,4518,492,0]An[4761,13,4762,1527,4708,492,0]An[4761,13,4762,1527,2407,492,0]An[4761,13,4762,2459,4712,492,0]An[4761,13,4762,2459,2418,492,0]An[4761,13,4762,2459,4154,492,0]An[4761,13,4762,2459,4004,492,0]An[4761,13,4762,2459,3354,492,0]An[4761,13,4762,1918,4310,492,0]An[4761,13,4762,1918,4532,492,0]An[4761,13,4762,1918,5053,492,0]An[4761,13,4762,1918,2420,492,0]An[4761,13,4762,1918,4804,492,0]An[4761,13,4762,1945,4522,492,0]An[4761,13,4762,1945,4287,492,0]An[4761,13,4762,1945,4157,492,0]An[4761,13,4762,1945,4162,492,0]Ak[4761,13,4762,0,4429,492,0]Ak[4761,13,4762,0,4864,492,0]An[4761,13,4762,4328,3335,492,0]An[4761,13,4762,4328,5146,492,0]An[4761,13,4762,4895,4738,492,0]Am[4761,13,4762,4762,490,492,0]Am[4761,13,4762,4762,122,492,0]An[4761,13,4762,4762,2437,492,0]An[4761,13,4762,4762,2446,492,0]An[4761,13,4762,4762,3144,492,0]An[4761,13,1851,1851,4321,492,0]An[4761,13,5006,5006,4905,492,0]An[4761,13,5006,5006,1352,492,0]An[4761,13,1856,1856,1999,492,0]Am[4761,13,1856,1856,477,492,0]An[4761,13,1856,1856,5113,492,0]An[4761,13,1856,1856,4013,492,0]An[4761,13,1856,1856,4870,492,0]An[4761,13,1935,1935,3366,492,0]An[4761,13,1935,1935,4291,492,0]An[4761,13,1935,1935,4734,492,0]An[4761,13,1935,1935,4736,492,0]An[4761,13,1935,1935,4961,492,0]Ak[4761,13,1935,0,1351,492,0]Al[4761,13,3726,137,116,492,0]Am[4761,13,3726,137,3135,492,0]An[4761,13,3726,1522,1338,492,0]An[4761,13,3726,1522,4015,492,0]An[4761,13,3726,2507,2004,492,0]An[4761,13,3726,2507,4308,492,0]An[4761,13,3726,3726,4796,492,0]An[4761,13,2135,2135,1353,492,0]An[4761,13,2135,2135,1356,492,0]An[1058,13,1059,1059,3137,492,0]An[1058,13,1059,1059,4899,492,0]Am[1058,13,1845,1845,130,492,0]An[1058,13,1553,1553,1371,492,0]An[1058,13,1553,1553,4038,492,0]An[1058,13,1553,1553,4528,492,0]An[1058,13,4248,4248,5098,492,0]An[1058,13,4248,4248,5099,492,0]Am[1058,13,1940,1940,131,492,0]Am[3994,13,3995,3995,120,492,0]An[3994,13,3995,3995,2404,492,0]Ah[3974,5,3975,3181,0,0,0]Ah[4761,5,4762,3181,0,0,0]Ah[3974,5,3725,1522,0,0,0]Ah[4761,5,3726,1522,0,0,0]Al[1058,15,1059,1059,4899,0,0]Ah[3974,6,1934,1525,0,0,0]Ah[4761,6,1935,1525,0,0,0]Ae[4761,6,5006,0,0,0,0]Ae[4761,6,2135,0,0,0,0]Ah[4761,6,4762,3731,0,0,0]1Al[4761,15,4762,1527,3338,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Al[3974,15,3975,4110,3165,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,1083,3166,0,0]Al[4761,15,4762,3731,4520,0,0]Al[4761,15,4762,1918,4532,0,0]0Ae[1374,5,1551,0,0,0,0]Ae[1058,5,1553,0,0,0,0]Ab[1374,3,0,0,0,0,0]Ai[3974,12,3725,0,4793,0,0]Al[3974,13,3725,0,3134,4793,0]Al[3974,13,3725,0,4014,4793,0]Al[3974,13,3725,0,4307,4793,0]Al[4761,12,3726,3726,4794,0,0]An[4761,13,3726,137,3135,4794,0]Ao[4761,13,3726,1522,4015,4794,0]Ao[4761,13,3726,2507,4308,4794,0]Al[1058,13,1059,1059,4899,0,0]Ai[3974,14,3975,0,1333,0,0]Ai[3974,14,3975,0,2448,0,0]Ai[3974,14,3975,0,2030,0,0]Al[4761,14,4762,3181,1334,0,0]Al[4761,14,4762,1552,2449,0,0]Al[4761,14,4762,1088,2031,0,0]Al[4761,14,4762,2459,4004,0,0]An[1374,13,1375,1375,129,1335,0]Ao[1374,13,1375,1375,4006,1335,0]Ao[3974,13,3975,3730,3325,1335,0]Ao[3974,13,3975,3730,4967,1335,0]Ao[3974,13,3975,3730,4805,1335,0]Ao[3974,13,3975,2458,2413,1335,0]Ao[3974,13,3975,2458,4288,1335,0]An[3974,13,3975,1057,482,1335,0]Ao[3974,13,3975,1057,4431,1335,0]Ao[3974,13,3975,4110,4424,1335,0]Ao[3974,13,3975,4110,3165,1335,0]Ao[3974,13,3975,4110,3352,1335,0]Ao[3974,13,3975,4110,4423,1335,0]Ao[3974,13,3975,4115,3370,1335,0]Ao[3974,13,3975,4115,4195,1335,0]Al[3974,13,3975,0,1613,1335,0]Al[3974,13,3975,0,4964,1335,0]Al[3974,13,3975,0,1596,1335,0]Al[3974,13,3975,0,2448,1335,0]Al[3974,13,3975,0,3331,1335,0]Al[3974,13,3975,0,3162,1335,0]Al[3974,13,3975,0,4523,1335,0]Al[3974,13,3975,0,2030,1335,0]Al[3974,13,3975,0,4147,1335,0]Al[3974,13,3975,0,4519,1335,0]Al[3974,13,3975,0,4540,1335,0]Al[3974,13,3975,0,4829,1335,0]Al[3974,13,3975,0,4844,1335,0]Al[3974,13,3975,0,4019,1335,0]Al[3974,13,3975,0,4400,1335,0]Al[3974,13,3975,0,4189,1335,0]Al[3974,13,3975,0,5021,1335,0]Al[3974,13,3975,0,4141,1335,0]Al[3974,13,3975,0,4009,1335,0]Al[3974,13,3975,0,3145,1335,0]Al[3974,13,3975,0,4516,1335,0]Al[3974,13,3975,0,4706,1335,0]Al[3974,13,3975,0,4153,1335,0]Al[3974,13,3975,0,4156,1335,0]Al[3974,13,3975,0,4161,1335,0]Al[3974,13,3975,0,4863,1335,0]Al[3974,13,3975,0,3334,1335,0]Ak[3974,13,3975,0,489,1335,0]Ak[3974,13,3975,0,121,1335,0]Al[3974,13,3975,0,2436,1335,0]Ak[3974,13,3725,0,115,1335,0]Al[3974,13,3725,0,3134,1335,0]Al[3974,13,3725,0,1337,1335,0]Al[3974,13,3725,0,4014,1335,0]Al[3974,13,3725,0,2003,1335,0]Al[3974,13,3725,0,4307,1335,0]Al[3974,13,1934,0,4960,1335,0]Al[3974,13,1934,0,1350,1335,0]Ao[4761,13,4762,1971,1614,1335,0]Ao[4761,13,4762,1971,4965,1335,0]Ao[4761,13,4762,3190,4806,1335,0]Ao[4761,13,4762,1537,1597,1335,0]Ao[4761,13,4762,1537,4185,1335,0]Ao[4761,13,4762,1537,3346,1335,0]Ao[4761,13,4762,1537,3161,1335,0]Ao[4761,13,4762,1537,4860,1335,0]Ao[4761,13,4762,1537,3321,1335,0]Ao[4761,13,4762,1552,2449,1335,0]Ao[4761,13,4762,1552,3332,1335,0]Ao[4761,13,4762,1552,3163,1335,0]Ao[4761,13,4762,1552,4524,1335,0]Ao[4761,13,4762,1083,3166,1335,0]Ao[4761,13,4762,1083,4315,1335,0]Ao[4761,13,4762,1088,2031,1335,0]Ao[4761,13,4762,1088,3371,1335,0]Ao[4761,13,4762,1088,4018,1335,0]Ao[4761,13,4762,2164,4425,1335,0]Ao[4761,13,4762,2164,4151,1335,0]Ao[4761,13,4762,3731,4520,1335,0]Ao[4761,13,4762,3731,3158,1335,0]Ao[4761,13,4762,3731,4391,1335,0]Ao[4761,13,4762,3731,5050,1335,0]Ao[4761,13,4762,3308,4541,1335,0]Ao[4761,13,4762,3308,4830,1335,0]Ao[4761,13,4762,3308,4845,1335,0]Ao[4761,13,4762,3308,4020,1335,0]Ao[4761,13,4762,1853,4401,1335,0]Ao[4761,13,4762,1853,4191,1335,0]Ao[4761,13,4762,1853,5022,1335,0]Ao[4761,13,4762,1853,4143,1335,0]Ao[4761,13,4762,1853,4010,1335,0]Ao[4761,13,4762,1527,3146,1335,0]Ao[4761,13,4762,1527,3338,1335,0]Ao[4761,13,4762,1527,3360,1335,0]Ao[4761,13,4762,1527,4158,1335,0]Ao[4761,13,4762,1527,4518,1335,0]Ao[4761,13,4762,1527,4708,1335,0]Ao[4761,13,4762,1527,2407,1335,0]Ao[4761,13,4762,2459,4712,1335,0]Ao[4761,13,4762,2459,4154,1335,0]Ao[4761,13,4762,1918,4532,1335,0]Ao[4761,13,4762,1918,5053,1335,0]Ao[4761,13,4762,1918,2420,1335,0]Ao[4761,13,4762,1918,4804,1335,0]Ao[4761,13,4762,1945,4157,1335,0]Ao[4761,13,4762,1945,4162,1335,0]Al[4761,13,4762,0,4429,1335,0]Al[4761,13,4762,0,4864,1335,0]Ao[4761,13,4762,4328,3335,1335,0]Ao[4761,13,4762,4328,5146,1335,0]An[4761,13,4762,4762,490,1335,0]An[4761,13,4762,4762,122,1335,0]Ao[4761,13,4762,4762,2437,1335,0]Ao[4761,13,4762,4762,3144,1335,0]Ao[4761,13,1851,1851,4321,1335,0]Ao[4761,13,5006,5006,4905,1335,0]Ao[4761,13,5006,5006,1352,1335,0]Ao[4761,13,1935,1935,4961,1335,0]Al[4761,13,1935,0,1351,1335,0]Am[4761,13,3726,137,116,1335,0]An[4761,13,3726,137,3135,1335,0]Ao[4761,13,3726,1522,1338,1335,0]Ao[4761,13,3726,1522,4015,1335,0]Ao[4761,13,3726,2507,2004,1335,0]Ao[4761,13,3726,2507,4308,1335,0]Ao[4761,13,2135,2135,1356,1335,0]Ao[4465,13,4466,4466,2434,1335,0]Ao[4465,13,4466,4466,2447,1335,0]Ao[4465,13,4466,4466,4163,1335,0]Ao[1058,13,1059,1059,4899,1335,0]Ao[1058,13,1553,1553,4528,1335,0]Al[3974,13,3975,3730,3325,0,0]Al[3974,13,3975,2458,2413,0,0]Ak[3974,13,3975,1057,482,0,0]Al[4761,13,4762,3731,3326,0,0]Al[3974,13,3975,0,4156,1341,0]Ao[4761,13,4762,1945,4157,1341,0]Ai[4761,14,3159,0,4298,0,0]Ai[4761,14,4762,0,4429,0,0]Ai[3974,13,3975,0,2436,0,0]Al[4761,13,4762,4762,2437,0,0]Ai[3974,13,3975,0,4161,0,0]Al[4761,13,4762,1945,4162,0,0]Ai[3974,14,1855,0,1998,0,0]Al[4761,14,1856,1856,1999,0,0]Al[4761,12,4762,1918,4835,0,0]Ao[4761,13,4762,1918,5171,4835,0]Al[4761,13,5006,5006,4905,0,0]Ah[3974,5,3975,2458,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Al[1374,15,1375,1375,4006,0,0]0Al[4761,15,4762,1537,3346,0,0]Ah[4761,5,4762,1083,0,0,0]Ah[3974,15,3975,0,489,0,0]Ak[4761,15,4762,4762,490,0,0]Al[3994,15,3995,3995,2404,0,0]Al[4761,15,2135,2135,1353,0,0]87Ah[3974,5,3975,3730,0,0,0]Al[4761,15,4762,1537,3161,0,0]Al[4761,15,4762,2164,4036,0,0]Ah[3974,5,3975,1537,0,0,0]Ah[4761,5,4762,1537,0,0,0]Ae[4761,8,1851,0,0,0,0]987>Al[4761,15,4762,3731,1355,0,0]>Ah[3974,5,3975,1971,0,0,0]Ah[4761,5,4762,1971,0,0,0]Al[3974,13,3975,2458,2413,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2164,2024,0,0]Al[4761,13,4762,2459,2414,0,0]An[1374,13,1375,1375,129,1576,0]Ao[1374,13,1375,1375,4006,1576,0]Ao[1374,13,1551,1551,4037,1576,0]Ao[1374,13,1551,1551,1370,1576,0]Ao[1374,13,1551,1551,4426,1576,0]Al[499,13,500,500,4007,1576,0]Ao[4043,13,4044,4044,5054,1576,0]Ao[3974,13,3975,3730,4545,1576,0]Ao[3974,13,3975,3730,2427,1576,0]Al[3974,13,3975,0,4279,1576,0]Ao[3974,13,3975,1057,2428,1576,0]Ao[3974,13,3975,1057,4704,1576,0]Ao[3974,13,3975,3730,3323,1576,0]Ao[3974,13,3975,3730,3325,1576,0]Ao[3974,13,3975,3730,2006,1576,0]Ao[3974,13,3975,3730,1589,1576,0]Ao[3974,13,3975,3730,3127,1576,0]Ao[3974,13,3975,3730,2401,1576,0]Ao[3974,13,3975,3730,4181,1576,0]Ao[3974,13,3975,3730,4186,1576,0]Ao[3974,13,3975,3730,4967,1576,0]Ao[3974,13,3975,3730,4805,1576,0]Ao[3974,13,3975,2458,2413,1576,0]Ao[3974,13,3975,2458,4288,1576,0]Ao[3974,13,3975,2458,1570,1576,0]Ao[3974,13,3975,2485,2417,1576,0]Ao[3974,13,3975,2485,3353,1576,0]Ao[3974,13,3975,2485,4405,1576,0]An[3974,13,3975,1057,482,1576,0]Ao[3974,13,3975,1057,4431,1576,0]Ao[3974,13,3975,4110,3350,1576,0]Ao[3974,13,3975,4110,4906,1576,0]Ao[3974,13,3975,4110,4424,1576,0]Ao[3974,13,3975,4110,3165,1576,0]Ao[3974,13,3975,4110,3352,1576,0]Ao[3974,13,3975,4110,4423,1576,0]Ao[3974,13,3975,1564,4196,1576,0]Ao[3974,13,3975,1100,4032,1576,0]Ao[3974,13,3975,4115,3356,1576,0]Ao[3974,13,3975,4115,3370,1576,0]Ao[3974,13,3975,4115,4195,1576,0]Ao[3974,13,3975,4115,2020,1576,0]Al[3974,13,3975,0,3347,1576,0]Al[3974,13,3975,0,4319,1576,0]Al[3974,13,3975,0,4537,1576,0]Al[3974,13,3975,0,4173,1576,0]Al[3974,13,3975,0,4414,1576,0]Al[3974,13,3975,0,1613,1576,0]Al[3974,13,3975,0,4964,1576,0]Al[3974,13,3975,0,1333,1576,0]Al[3974,13,3975,0,1596,1576,0]Al[3974,13,3975,0,2448,1576,0]Al[3974,13,3975,0,3331,1576,0]Al[3974,13,3975,0,3162,1576,0]Al[3974,13,3975,0,4523,1576,0]Al[3974,13,3975,0,2030,1576,0]Al[3974,13,3975,0,4147,1576,0]Al[3974,13,3975,0,4519,1576,0]Al[3974,13,3975,0,4533,1576,0]Al[3974,13,3975,0,4540,1576,0]Al[3974,13,3975,0,4829,1576,0]Al[3974,13,3975,0,4844,1576,0]Al[3974,13,3975,0,4019,1576,0]Al[3974,13,3975,0,4400,1576,0]Al[3974,13,3975,0,4189,1576,0]Al[3974,13,3975,0,5021,1576,0]Al[3974,13,3975,0,4141,1576,0]Al[3974,13,3975,0,4009,1576,0]Al[3974,13,3975,0,3145,1576,0]Al[3974,13,3975,0,4516,1576,0]Al[3974,13,3975,0,4706,1576,0]Al[3974,13,3975,0,4153,1576,0]Al[3974,13,3975,0,4156,1576,0]Al[3974,13,3975,0,4161,1576,0]Al[3974,13,3975,0,4863,1576,0]Al[3974,13,3975,0,3334,1576,0]Al[3974,13,3975,0,4737,1576,0]Ak[3974,13,3975,0,489,1576,0]Ak[3974,13,3975,0,121,1576,0]Al[3974,13,3975,0,2436,1576,0]Al[3974,13,3975,0,2445,1576,0]Ak[3974,13,3725,0,115,1576,0]Al[3974,13,3725,0,3134,1576,0]Al[3974,13,3725,0,1337,1576,0]Al[3974,13,3725,0,4014,1576,0]Al[3974,13,3725,0,2003,1576,0]Al[3974,13,3725,0,4307,1576,0]Al[3974,13,3725,0,4795,1576,0]Al[3974,13,1855,0,1998,1576,0]Ak[3974,13,1855,0,476,1576,0]Al[3974,13,1855,0,5112,1576,0]Al[3974,13,1855,0,4012,1576,0]Al[3974,13,1855,0,4867,1576,0]Al[3974,13,1934,0,3365,1576,0]Al[3974,13,1934,0,4290,1576,0]Al[3974,13,1934,0,4733,1576,0]Al[3974,13,1934,0,4735,1576,0]Al[3974,13,1934,0,4960,1576,0]Al[3974,13,1934,0,1350,1576,0]Ao[4761,13,4762,4550,4280,1576,0]Ao[4761,13,4762,3190,3140,1576,0]Ao[4761,13,4762,1083,1578,1576,0]Ao[4761,13,4762,2164,2024,1576,0]Ao[4761,13,4762,2164,3351,1576,0]Ao[4761,13,4762,2164,3357,1576,0]Ao[4761,13,4762,3731,3326,1576,0]Ao[4761,13,4762,1079,3348,1576,0]Ao[4761,13,4762,1853,4320,1576,0]Ao[4761,13,4762,2459,2414,1576,0]Ao[4761,13,4762,2459,4286,1576,0]Ao[4761,13,4762,1918,5171,1576,0]Ao[4761,13,4762,4328,5141,1576,0]Ao[4761,13,4762,1563,4538,1576,0]Ao[4761,13,4762,1563,4174,1576,0]Ao[4761,13,4762,1563,4415,1576,0]Ao[4761,13,4762,1971,1614,1576,0]Ao[4761,13,4762,1971,4965,1576,0]Ao[4761,13,4762,3181,1334,1576,0]Ao[4761,13,4762,3190,4806,1576,0]Ao[4761,13,4762,1537,1597,1576,0]Ao[4761,13,4762,1537,4185,1576,0]Ao[4761,13,4762,1537,3346,1576,0]Ao[4761,13,4762,1537,3161,1576,0]Ao[4761,13,4762,1537,4860,1576,0]Ao[4761,13,4762,1537,3321,1576,0]Ao[4761,13,4762,1552,2449,1576,0]Ao[4761,13,4762,1552,3332,1576,0]Ao[4761,13,4762,1552,3163,1576,0]Ao[4761,13,4762,1552,4524,1576,0]Ao[4761,13,4762,1083,3166,1576,0]Ao[4761,13,4762,1083,4315,1576,0]Ao[4761,13,4762,1088,2031,1576,0]Ao[4761,13,4762,1088,3371,1576,0]Ao[4761,13,4762,1088,4018,1576,0]Ao[4761,13,4762,2164,4036,1576,0]Ao[4761,13,4762,2164,4425,1576,0]Ao[4761,13,4762,2164,4151,1576,0]Ao[4761,13,4762,3731,4520,1576,0]Ao[4761,13,4762,3731,3158,1576,0]Ao[4761,13,4762,3731,4391,1576,0]Ao[4761,13,4762,3731,5050,1576,0]Ao[4761,13,4762,3731,1355,1576,0]Ao[4761,13,4762,1079,4534,1576,0]Ao[4761,13,4762,3308,4541,1576,0]Ao[4761,13,4762,3308,4830,1576,0]Ao[4761,13,4762,3308,4845,1576,0]Ao[4761,13,4762,3308,4020,1576,0]Ao[4761,13,4762,1853,4401,1576,0]Ao[4761,13,4762,1853,4191,1576,0]Ao[4761,13,4762,1853,5022,1576,0]Ao[4761,13,4762,1853,4143,1576,0]Ao[4761,13,4762,1853,4010,1576,0]Ao[4761,13,4762,1527,3146,1576,0]Ao[4761,13,4762,1527,4406,1576,0]Ao[4761,13,4762,1527,3338,1576,0]Ao[4761,13,4762,1527,3360,1576,0]Ao[4761,13,4762,1527,4158,1576,0]Ao[4761,13,4762,1527,4518,1576,0]Ao[4761,13,4762,1527,4708,1576,0]Ao[4761,13,4762,1527,2407,1576,0]Ao[4761,13,4762,2459,4712,1576,0]Ao[4761,13,4762,2459,2418,1576,0]Ao[4761,13,4762,2459,4154,1576,0]Ao[4761,13,4762,2459,4004,1576,0]Ao[4761,13,4762,2459,3354,1576,0]Ao[4761,13,4762,1918,4310,1576,0]Ao[4761,13,4762,1918,4532,1576,0]Ao[4761,13,4762,1918,5053,1576,0]Ao[4761,13,4762,1918,2420,1576,0]Ao[4761,13,4762,1918,4804,1576,0]Ao[4761,13,4762,1945,4522,1576,0]Ao[4761,13,4762,1945,4287,1576,0]Ao[4761,13,4762,1945,4157,1576,0]Ao[4761,13,4762,1945,4162,1576,0]Al[4761,13,4762,0,4429,1576,0]Al[4761,13,4762,0,4864,1576,0]Ao[4761,13,4762,4328,3335,1576,0]Ao[4761,13,4762,4328,5146,1576,0]Ao[4761,13,4762,4895,4738,1576,0]An[4761,13,4762,4762,490,1576,0]An[4761,13,4762,4762,122,1576,0]Ao[4761,13,4762,4762,2437,1576,0]Ao[4761,13,4762,4762,2446,1576,0]Ao[4761,13,4762,4762,3144,1576,0]Ao[4761,13,1851,1851,4321,1576,0]Ao[4761,13,5006,5006,4905,1576,0]Ao[4761,13,5006,5006,1352,1576,0]Ao[4761,13,1856,1856,1999,1576,0]An[4761,13,1856,1856,477,1576,0]Ao[4761,13,1856,1856,5113,1576,0]Ao[4761,13,1856,1856,4013,1576,0]Ao[4761,13,1856,1856,4870,1576,0]Ao[4761,13,1935,1935,3366,1576,0]Ao[4761,13,1935,1935,4291,1576,0]Ao[4761,13,1935,1935,4734,1576,0]Ao[4761,13,1935,1935,4736,1576,0]Ao[4761,13,1935,1935,4961,1576,0]Al[4761,13,1935,0,1351,1576,0]Am[4761,13,3726,137,116,1576,0]An[4761,13,3726,137,3135,1576,0]Ao[4761,13,3726,1522,1338,1576,0]Ao[4761,13,3726,1522,4015,1576,0]Ao[4761,13,3726,2507,2004,1576,0]Ao[4761,13,3726,2507,4308,1576,0]Ao[4761,13,3726,3726,4796,1576,0]Ao[4761,13,2135,2135,1353,1576,0]Ao[4761,13,2135,2135,1356,1576,0]Ao[4465,13,4466,4466,3369,1576,0]An[4465,13,4466,397,4430,1576,0]Ao[4465,13,4466,4466,2434,1576,0]Ao[4465,13,4466,4466,2447,1576,0]Ao[4465,13,4466,4466,4163,1576,0]Ao[1058,13,1059,1059,3137,1576,0]Ao[1058,13,1059,1059,4899,1576,0]An[1058,13,1845,1845,130,1576,0]Ao[1058,13,1553,1553,1371,1576,0]Ao[1058,13,1553,1553,4038,1576,0]Ao[1058,13,1553,1553,4528,1576,0]Ao[1058,13,4248,4248,5098,1576,0]Ao[1058,13,4248,4248,5099,1576,0]An[1058,13,1940,1940,131,1576,0]An[3994,13,3995,3995,120,1576,0]Ao[3994,13,3995,3995,2404,1576,0]Ai[3974,14,3975,0,2030,0,0]Al[4761,14,4762,1088,2031,0,0]Ak[1374,14,1375,1375,129,0,0]Ac[499,7,500,0,0,0,0]Al[3974,13,3975,2458,2413,0,0]Ae[1058,2,1059,0,0,0,0]Ae[4824,7,4825,0,0,0,0]Al[3974,13,3975,3730,3325,0,0]Ae[4761,2,4762,0,0,0,0]Al[4761,13,4762,2459,2414,0,0]Ah[3974,2,3975,4762,0,0,0]Ai[3974,13,3975,0,4863,0,0]Ai[3974,14,3975,0,3331,0,0]Ai[3974,14,3975,0,4189,0,0]5Ai[4761,13,4762,0,4864,0,0]Al[4761,14,4762,1552,3332,0,0]Al[4761,14,4762,1853,4191,0,0]Ai[4761,14,4762,0,4429,0,0]Al[3974,13,3975,0,1613,3141,0]Al[3974,13,3975,0,1596,3141,0]Al[3974,13,3975,0,3145,3141,0]Al[3974,13,3975,0,4863,3141,0]Al[3974,13,3975,0,2436,3141,0]Ao[4761,13,4762,1971,1614,3141,0]Ao[4761,13,4762,1537,1597,3141,0]Ao[4761,13,4762,1527,3146,3141,0]Ao[4761,13,4762,1527,3338,3141,0]Al[4761,13,4762,1918,4532,0,0]Al[4761,13,4762,0,4864,3141,0]Ao[4761,13,4762,4762,2437,3141,0]Al[4761,12,1851,1851,3141,0,0]Al[4465,14,4466,4466,2434,0,0]Al[3974,13,3975,0,1613,3142,0]Al[3974,13,3975,0,1596,3142,0]Al[3974,13,3975,0,3145,3142,0]Al[3974,13,3975,0,4863,3142,0]Al[3974,13,3975,0,2436,3142,0]Ao[4761,13,4762,1971,1614,3142,0]Ao[4761,13,4762,1537,1597,3142,0]Ao[4761,13,4762,1527,3146,3142,0]Ao[4761,13,4762,1527,3338,3142,0]Ao[4761,13,4762,1527,2407,3142,0]Al[4761,13,4762,0,4864,3142,0]Ao[4761,13,4762,4762,2437,3142,0]Al[4761,12,1851,1851,3142,0,0]Al[3974,13,3975,4115,3356,0,0]Al[4761,13,4762,2164,3357,0,0]Al[1058,13,1553,1553,4038,0,0]Al[4761,13,4762,2459,2414,0,0]Ah[3974,14,1855,0,476,0,0]Ak[4761,14,1856,1856,477,0,0]Al[3974,13,3975,2485,2417,0,0]3Ai[3974,12,3975,0,3367,0,0]Al[3974,13,3975,0,4414,3367,0]Al[3974,13,3975,0,4737,3367,0]Al[4761,12,4762,1563,3368,0,0]Ao[4761,13,4762,1563,4415,3368,0]Ao[4761,13,4762,4895,4738,3368,0]Al[1058,14,1059,1059,3137,0,0]Ai[3974,14,3975,0,1333,0,0]Al[4761,14,4762,3181,1334,0,0]Ai[3974,13,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,13,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Ah[3974,14,1855,0,476,0,0]Ak[4761,14,1856,1856,477,0,0]Ai[4761,14,4531,0,4741,0,0]Ai[3974,14,3975,0,4516,0,0]Ai[3974,14,3975,0,4706,0,0]Al[4761,14,4762,1527,4518,0,0]Al[4761,14,4762,1527,4708,0,0]Ai[4761,14,4762,0,4429,0,0]Ai[4761,14,1354,0,4393,0,0]Al[3974,13,3975,2458,2413,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2459,2414,0,0]Ai[3974,14,1855,0,1998,0,0]Al[4761,14,4762,4762,3144,0,0]Al[4761,14,1856,1856,1999,0,0]Ae[3974,4,3975,0,0,0,0]Ah[3974,2,1855,1856,0,0,0]Ae[4761,4,4762,0,0,0,0]Ae[4761,2,1856,0,0,0,0]Ac[499,7,500,0,0,0,0]:Ae[1058,2,1059,0,0,0,0]Ae[4824,7,4825,0,0,0,0]Ao[3974,13,3975,3730,4805,1343,0]Ao[3974,13,3975,4110,4424,1343,0]Ao[3974,13,3975,4110,3165,1343,0]Ao[3974,13,3975,4110,3352,1343,0]Ao[3974,13,3975,4110,4423,1343,0]Ao[3974,13,3975,4115,3370,1343,0]Ao[3974,13,3975,4115,4195,1343,0]Al[3974,13,3975,0,4519,1343,0]Ao[4761,13,4762,3731,4520,1343,0]Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Al[4465,14,4466,4466,2447,0,0]Ak[3974,13,3975,1057,482,0,0]Ai[4761,14,3159,0,4182,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]Al[4465,14,4466,4466,2434,0,0]Ai[3974,14,3975,0,3162,0,0]Ai[3974,14,3975,0,4523,0,0]Al[4761,14,4762,1552,3163,0,0]Al[4761,14,4762,1552,4524,0,0]3210Ai[3974,12,1855,0,1994,0,0]Al[4761,12,1856,1856,1995,0,0]Ai[3974,12,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,12,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Ai[3974,10,1855,1856,0,0,0]Af[4761,10,1856,0,0,0,0]Al[4761,15,4762,3190,4806,0,0]Al[4761,15,4762,1083,3166,0,0]Al[4761,15,4762,1088,3371,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]65Al[4761,15,4762,1918,4532,0,0]Ah[3974,5,1855,1856,0,0,0]Ae[4761,5,1856,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[3974,5,3725,2507,0,0,0]Ah[4761,5,3726,2507,0,0,0]Al[1058,15,1059,1059,4899,0,0]Ah[3974,5,3975,3730,0,0,0]5454Ai[3974,10,3975,2156,0,0,0]Ai[3974,10,1855,1856,0,0,0]Af[4761,10,1856,0,0,0,0]Al[3974,15,3975,4115,3370,0,0]Ah[3974,5,3975,4115,0,0,0]Al[4761,15,4762,1088,3371,0,0]Al[4761,15,4762,1088,4018,0,0]Al[4761,15,4762,2164,4036,0,0]Ah[4761,5,4762,2164,0,0,0]Al[3974,15,3975,4110,4424,0,0]Al[4761,15,4762,1527,3338,0,0]Ah[3974,5,3975,1088,0,0,0]Ah[4761,5,4762,1088,0,0,0]Al[4761,15,4762,1918,2420,0,0]Ai[3974,12,3975,0,5012,0,0]Al[4761,12,4762,4328,5013,0,0]Ao[4761,13,4762,4328,5141,5013,0]Ao[4761,13,4762,4328,5146,5013,0]Ak[1374,14,1375,1375,129,0,0]An[1374,13,1375,1375,129,4392,0]Ao[1058,13,1059,1059,3137,4392,0]An[1058,13,1845,1845,130,4392,0]An[1058,13,1940,1940,131,4392,0]An[3994,13,3995,3995,120,4392,0]Ak[3994,14,3995,3995,120,0,0]Al[3974,13,3975,0,1613,3322,0]Al[3974,13,3975,0,1596,3322,0]Al[3974,13,3975,0,4019,3322,0]Al[3974,13,3975,0,4156,3322,0]Ak[3974,13,3975,0,489,3322,0]Ak[3974,13,3975,0,121,3322,0]Al[3974,13,3975,0,2436,3322,0]Ao[4761,13,4762,1971,1614,3322,0]Ao[4761,13,4762,3190,4806,3322,0]Ao[4761,13,4762,1537,1597,3322,0]Ao[4761,13,4762,1083,3166,3322,0]Ao[4761,13,4762,1083,4315,3322,0]Ao[4761,13,4762,1088,3371,3322,0]Ao[4761,13,4762,3308,4020,3322,0]Ao[4761,13,4762,2459,4712,3322,0]Ao[4761,13,4762,1918,4804,3322,0]Ao[4761,13,4762,1945,4157,3322,0]An[4761,13,4762,4762,490,3322,0]An[4761,13,4762,4762,122,3322,0]Ao[4761,13,4762,4762,2437,3322,0]Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,2459,2414,0,0]Ai[3974,12,1855,0,3155,0,0]Ai[3974,12,1855,0,3148,0,0]Al[4761,12,1856,1856,3156,0,0]Al[4761,12,1856,1856,3149,0,0]Ao[1374,13,1551,1551,4037,1989,0]Ao[1374,13,1551,1551,1370,1989,0]Al[499,13,500,500,4007,1989,0]Al[3974,13,3975,0,4279,1989,0]Ao[3974,13,3975,2458,4288,1989,0]Al[3974,13,3975,0,1613,1989,0]Al[3974,13,3975,0,1596,1989,0]Al[3974,13,3975,0,2448,1989,0]Al[3974,13,3975,0,3331,1989,0]Al[3974,13,3975,0,3162,1989,0]Al[3974,13,3975,0,4523,1989,0]Al[3974,13,3975,0,2030,1989,0]Al[3974,13,3975,0,4147,1989,0]Al[3974,13,3975,0,4540,1989,0]Al[3974,13,3975,0,4829,1989,0]Al[3974,13,3975,0,4844,1989,0]Al[3974,13,3975,0,4019,1989,0]Al[3974,13,3975,0,4400,1989,0]Al[3974,13,3975,0,5021,1989,0]Al[3974,13,3975,0,4737,1989,0]Al[3974,13,3725,0,3134,1989,0]Al[3974,13,3725,0,4014,1989,0]Al[3974,13,3725,0,4307,1989,0]Al[3974,13,3725,0,4795,1989,0]Ao[4761,13,4762,4550,4280,1989,0]Ao[4761,13,4762,4328,5141,1989,0]Ao[4761,13,4762,1971,1614,1989,0]Ao[4761,13,4762,1537,1597,1989,0]Ao[4761,13,4762,1537,3346,1989,0]Ao[4761,13,4762,1537,3321,1989,0]Ao[4761,13,4762,1552,2449,1989,0]Ao[4761,13,4762,1552,3332,1989,0]Ao[4761,13,4762,1552,3163,1989,0]Ao[4761,13,4762,1552,4524,1989,0]Ao[4761,13,4762,1088,2031,1989,0]Ao[4761,13,4762,2164,4425,1989,0]Ao[4761,13,4762,2164,4151,1989,0]Ao[4761,13,4762,3308,4541,1989,0]Ao[4761,13,4762,3308,4830,1989,0]Ao[4761,13,4762,3308,4845,1989,0]Ao[4761,13,4762,3308,4020,1989,0]Ao[4761,13,4762,1853,4401,1989,0]Ao[4761,13,4762,1853,5022,1989,0]Ao[4761,13,4762,4328,5146,1989,0]Ao[4761,13,4762,4895,4738,1989,0]An[4761,13,3726,137,3135,1989,0]Ao[4761,13,3726,1522,4015,1989,0]Ao[4761,13,3726,2507,4308,1989,0]Ao[4761,13,3726,3726,4796,1989,0]Ao[4465,13,4466,4466,2434,1989,0]Ao[1058,13,1553,1553,1371,1989,0]Ao[1058,13,1553,1553,4528,1989,0]Al[4761,13,4762,1945,4287,0,0]Al[4761,13,4762,1918,4310,0,0]Al[4761,13,4762,1918,4532,0,0]Al[4761,14,4762,1918,5053,0,0]Ai[4761,14,4803,0,4865,0,0]Ah[4761,14,4531,0,478,0,0]Ai[4761,14,4531,0,1365,0,0]Ai[4761,14,4531,0,1367,0,0]Ai[4761,14,4531,0,4741,0,0]Ai[3974,12,1855,0,3155,0,0]Ai[3974,12,1855,0,3148,0,0]Al[4761,12,1856,1856,3156,0,0]Al[4761,12,1856,1856,3149,0,0]Ae[4761,2,4762,0,0,0,0]Al[3974,13,3975,4110,3350,0,0]Al[3974,13,3975,4110,4906,0,0]Ai[3974,13,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,13,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]9854Al[3974,13,3975,3730,3325,0,0]Al[3974,13,3975,1057,2428,0,0]Al[4761,14,4762,1918,5053,0,0]Ai[4761,14,4803,0,4865,0,0]Ah[4761,14,4531,0,478,0,0]Ai[4761,14,4531,0,1365,0,0]Ai[4761,14,4531,0,4741,0,0]Al[3974,13,3975,4115,3356,0,0]Al[4761,13,4762,2164,3357,0,0]Ak[1374,14,1375,1375,129,0,0]Al[1374,14,1551,1551,1370,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,12,1855,0,3155,0,0]Al[4761,12,1856,1856,3156,0,0]Am[1374,13,1375,1375,129,113,0]An[1374,13,1375,1375,4006,113,0]An[1374,13,1551,1551,4037,113,0]An[1374,13,1551,1551,1370,113,0]An[1374,13,1551,1551,4426,113,0]Ak[499,13,500,500,4007,113,0]An[4043,13,4044,4044,5054,113,0]An[3974,13,3975,3730,4545,113,0]An[3974,13,3975,3730,2427,113,0]Ak[3974,13,3975,0,4279,113,0]An[3974,13,3975,1057,2428,113,0]An[3974,13,3975,1057,4704,113,0]An[3974,13,3975,3730,3323,113,0]An[3974,13,3975,3730,3325,113,0]An[3974,13,3975,3730,2006,113,0]An[3974,13,3975,3730,1589,113,0]An[3974,13,3975,3730,3127,113,0]An[3974,13,3975,3730,2401,113,0]An[3974,13,3975,3730,4181,113,0]An[3974,13,3975,3730,4186,113,0]An[3974,13,3975,3730,4967,113,0]An[3974,13,3975,3730,4805,113,0]An[3974,13,3975,2458,2413,113,0]An[3974,13,3975,2458,4288,113,0]An[3974,13,3975,2458,1570,113,0]An[3974,13,3975,2485,2417,113,0]An[3974,13,3975,2485,3353,113,0]An[3974,13,3975,2485,4405,113,0]Am[3974,13,3975,1057,482,113,0]An[3974,13,3975,1057,4431,113,0]An[3974,13,3975,4110,3350,113,0]An[3974,13,3975,4110,4906,113,0]An[3974,13,3975,4110,4424,113,0]An[3974,13,3975,4110,3165,113,0]An[3974,13,3975,4110,3352,113,0]An[3974,13,3975,4110,4423,113,0]An[3974,13,3975,1564,4196,113,0]An[3974,13,3975,1100,4032,113,0]An[3974,13,3975,4115,3356,113,0]An[3974,13,3975,4115,3370,113,0]An[3974,13,3975,4115,4195,113,0]An[3974,13,3975,4115,2020,113,0]Ak[3974,13,3975,0,3347,113,0]Ak[3974,13,3975,0,4319,113,0]Ak[3974,13,3975,0,4537,113,0]Ak[3974,13,3975,0,4173,113,0]Ak[3974,13,3975,0,4414,113,0]Ak[3974,13,3975,0,1613,113,0]Ak[3974,13,3975,0,4964,113,0]Ak[3974,13,3975,0,1333,113,0]Ak[3974,13,3975,0,1596,113,0]Ak[3974,13,3975,0,2448,113,0]Ak[3974,13,3975,0,3331,113,0]Ak[3974,13,3975,0,3162,113,0]Ak[3974,13,3975,0,4523,113,0]Ak[3974,13,3975,0,2030,113,0]Ak[3974,13,3975,0,4147,113,0]Ak[3974,13,3975,0,4519,113,0]Ak[3974,13,3975,0,4533,113,0]Ak[3974,13,3975,0,4540,113,0]Ak[3974,13,3975,0,4829,113,0]Ak[3974,13,3975,0,4844,113,0]Ak[3974,13,3975,0,4019,113,0]Ak[3974,13,3975,0,4400,113,0]Ak[3974,13,3975,0,4189,113,0]Ak[3974,13,3975,0,5021,113,0]Ak[3974,13,3975,0,4141,113,0]Ak[3974,13,3975,0,4009,113,0]Ak[3974,13,3975,0,3145,113,0]Ak[3974,13,3975,0,4516,113,0]Ak[3974,13,3975,0,4706,113,0]Ak[3974,13,3975,0,4153,113,0]Ak[3974,13,3975,0,4156,113,0]Ak[3974,13,3975,0,4161,113,0]Ak[3974,13,3975,0,4863,113,0]Ak[3974,13,3975,0,3334,113,0]Ak[3974,13,3975,0,4737,113,0]Aj[3974,13,3975,0,489,113,0]Aj[3974,13,3975,0,121,113,0]Ak[3974,13,3975,0,2436,113,0]Ak[3974,13,3975,0,2445,113,0]Aj[3974,13,3725,0,115,113,0]Ak[3974,13,3725,0,3134,113,0]Ak[3974,13,3725,0,1337,113,0]Ak[3974,13,3725,0,4014,113,0]Ak[3974,13,3725,0,2003,113,0]Ak[3974,13,3725,0,4307,113,0]Ak[3974,13,3725,0,4795,113,0]Ak[3974,13,1855,0,1998,113,0]Aj[3974,13,1855,0,476,113,0]Ak[3974,13,1855,0,5112,113,0]Ak[3974,13,1855,0,4012,113,0]Ak[3974,13,1855,0,4867,113,0]Ak[3974,13,1934,0,3365,113,0]Ak[3974,13,1934,0,4290,113,0]Ak[3974,13,1934,0,4733,113,0]Ak[3974,13,1934,0,4735,113,0]Ak[3974,13,1934,0,4960,113,0]Ak[3974,13,1934,0,1350,113,0]An[4761,13,4762,4550,4280,113,0]An[4761,13,4762,3190,3140,113,0]An[4761,13,4762,1083,1578,113,0]An[4761,13,4762,2164,2024,113,0]An[4761,13,4762,2164,3351,113,0]An[4761,13,4762,2164,3357,113,0]An[4761,13,4762,3731,3326,113,0]An[4761,13,4762,1079,3348,113,0]An[4761,13,4762,1853,4320,113,0]An[4761,13,4762,2459,2414,113,0]An[4761,13,4762,2459,4286,113,0]An[4761,13,4762,1918,5171,113,0]An[4761,13,4762,4328,5141,113,0]An[4761,13,4762,1563,4538,113,0]An[4761,13,4762,1563,4174,113,0]An[4761,13,4762,1563,4415,113,0]An[4761,13,4762,1971,1614,113,0]An[4761,13,4762,1971,4965,113,0]An[4761,13,4762,3181,1334,113,0]An[4761,13,4762,3190,4806,113,0]An[4761,13,4762,1537,1597,113,0]An[4761,13,4762,1537,4185,113,0]An[4761,13,4762,1537,3346,113,0]An[4761,13,4762,1537,3161,113,0]An[4761,13,4762,1537,4860,113,0]An[4761,13,4762,1537,3321,113,0]An[4761,13,4762,1552,2449,113,0]An[4761,13,4762,1552,3332,113,0]An[4761,13,4762,1552,3163,113,0]An[4761,13,4762,1552,4524,113,0]An[4761,13,4762,1083,3166,113,0]An[4761,13,4762,1083,4315,113,0]An[4761,13,4762,1088,2031,113,0]An[4761,13,4762,1088,3371,113,0]An[4761,13,4762,1088,4018,113,0]An[4761,13,4762,2164,4036,113,0]An[4761,13,4762,2164,4425,113,0]An[4761,13,4762,2164,4151,113,0]An[4761,13,4762,3731,4520,113,0]An[4761,13,4762,3731,3158,113,0]An[4761,13,4762,3731,4391,113,0]An[4761,13,4762,3731,5050,113,0]An[4761,13,4762,3731,1355,113,0]An[4761,13,4762,1079,4534,113,0]An[4761,13,4762,3308,4541,113,0]An[4761,13,4762,3308,4830,113,0]An[4761,13,4762,3308,4845,113,0]An[4761,13,4762,3308,4020,113,0]An[4761,13,4762,1853,4401,113,0]An[4761,13,4762,1853,4191,113,0]An[4761,13,4762,1853,5022,113,0]An[4761,13,4762,1853,4143,113,0]An[4761,13,4762,1853,4010,113,0]An[4761,13,4762,1527,3146,113,0]An[4761,13,4762,1527,4406,113,0]An[4761,13,4762,1527,3338,113,0]An[4761,13,4762,1527,3360,113,0]An[4761,13,4762,1527,4158,113,0]An[4761,13,4762,1527,4518,113,0]An[4761,13,4762,1527,4708,113,0]An[4761,13,4762,1527,2407,113,0]An[4761,13,4762,2459,4712,113,0]An[4761,13,4762,2459,2418,113,0]An[4761,13,4762,2459,4154,113,0]An[4761,13,4762,2459,4004,113,0]An[4761,13,4762,2459,3354,113,0]An[4761,13,4762,1918,4310,113,0]An[4761,13,4762,1918,4532,113,0]An[4761,13,4762,1918,5053,113,0]An[4761,13,4762,1918,2420,113,0]An[4761,13,4762,1918,4804,113,0]An[4761,13,4762,1945,4522,113,0]An[4761,13,4762,1945,4287,113,0]An[4761,13,4762,1945,4157,113,0]An[4761,13,4762,1945,4162,113,0]Ak[4761,13,4762,0,4429,113,0]Ak[4761,13,4762,0,4864,113,0]An[4761,13,4762,4328,3335,113,0]An[4761,13,4762,4328,5146,113,0]An[4761,13,4762,4895,4738,113,0]Am[4761,13,4762,4762,490,113,0]Am[4761,13,4762,4762,122,113,0]An[4761,13,4762,4762,2437,113,0]An[4761,13,4762,4762,2446,113,0]An[4761,13,4762,4762,3144,113,0]An[4761,13,1851,1851,4321,113,0]An[4761,13,5006,5006,4905,113,0]An[4761,13,5006,5006,1352,113,0]An[4761,13,1856,1856,1999,113,0]Am[4761,13,1856,1856,477,113,0]An[4761,13,1856,1856,5113,113,0]An[4761,13,1856,1856,4013,113,0]An[4761,13,1856,1856,4870,113,0]An[4761,13,1935,1935,3366,113,0]An[4761,13,1935,1935,4291,113,0]An[4761,13,1935,1935,4734,113,0]An[4761,13,1935,1935,4736,113,0]An[4761,13,1935,1935,4961,113,0]Ak[4761,13,1935,0,1351,113,0]Al[4761,13,3726,137,116,113,0]Am[4761,13,3726,137,3135,113,0]An[4761,13,3726,1522,1338,113,0]An[4761,13,3726,1522,4015,113,0]An[4761,13,3726,2507,2004,113,0]An[4761,13,3726,2507,4308,113,0]An[4761,13,3726,3726,4796,113,0]An[4761,13,2135,2135,1353,113,0]An[4761,13,2135,2135,1356,113,0]An[4465,13,4466,4466,3369,113,0]Am[4465,13,4466,397,4430,113,0]An[4465,13,4466,4466,2434,113,0]An[4465,13,4466,4466,2447,113,0]An[4465,13,4466,4466,4163,113,0]An[1058,13,1059,1059,3137,113,0]An[1058,13,1059,1059,4899,113,0]Am[1058,13,1845,1845,130,113,0]An[1058,13,1553,1553,1371,113,0]An[1058,13,1553,1553,4038,113,0]An[1058,13,1553,1553,4528,113,0]An[1058,13,4248,4248,5098,113,0]An[1058,13,4248,4248,5099,113,0]Am[1058,13,1940,1940,131,113,0]Am[3994,13,3995,3995,120,113,0]An[3994,13,3995,3995,2404,113,0]Al[3974,13,3975,3730,4967,0,0]Al[3974,13,3975,1057,4431,0,0]Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4143,0,0]Al[4761,14,4762,1918,5053,0,0]Ai[4761,14,4803,0,4865,0,0]Ah[4761,14,4531,0,478,0,0]Ai[4761,14,4531,0,1365,0,0]Ah[3974,5,3975,3730,0,0,0]Al[1374,15,1375,1375,4006,0,0]Ae[3994,6,3995,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1527,0,0,0]Al[3974,15,3975,3730,4805,0,0]Al[4761,15,4762,3190,4806,0,0]Ah[3974,5,3975,2458,0,0,0]Ah[4761,5,4762,2459,0,0,0]Al[4761,15,4762,2164,4036,0,0]Ah[3974,5,3975,2485,0,0,0]2Ah[4761,6,4762,1918,0,0,0]Ai[3974,19,3975,0,1613,0,0]Al[4761,19,4762,1971,1614,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]=Ai[3974,12,1855,0,4190,0,0]>Al[3974,13,1934,0,4735,4190,0]>Al[4761,12,1856,1856,4192,0,0]>Ao[4761,13,1935,1935,4736,4192,0]Ao[1058,13,4248,4248,5099,4192,0]An[3994,13,3995,3995,120,4021,0]Ao[3994,13,3995,3995,2404,4021,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1552,3163,0,0]Ai[3974,13,3975,0,4189,0,0]Al[4761,13,4762,1853,4191,0,0]Al[3974,13,3975,2156,4388,0,0]0>=Al[1058,13,1553,1553,4528,0,0]Ae[1058,2,1059,0,0,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2459,2418,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]43Ai[3974,14,3975,0,2445,0,0]Al[4761,14,4762,4762,2446,0,0]Al[4465,14,4466,4466,2447,0,0]Al[4761,13,4762,2164,2024,0,0]Al[4761,13,4762,4328,5141,0,0]Al[4761,13,4762,4328,5146,0,0]Al[3974,13,3975,4115,3356,0,0]Al[4761,13,4762,2164,3357,0,0]<;Al[1374,14,1551,1551,1370,0,0]Ai[499,14,500,500,4007,0,0]Ai[3974,13,1934,0,4733,0,0]Ai[3974,13,1934,0,4735,0,0]Al[4761,13,1935,1935,4734,0,0]Al[4761,13,1935,1935,4736,0,0]76Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Ah[3974,5,3975,4550,0,0,0]Ah[4761,5,4762,4550,0,0,0]Ai[3974,15,3975,0,4519,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,3731,4520,0,0]Al[4761,15,4762,2459,4154,0,0]Ah[4761,6,4762,2459,0,0,0]Ah[4761,5,4762,1945,0,0,0]Ah[3974,5,3975,2458,0,0,0]Al[3974,15,3975,4110,4423,0,0]Ah[3974,5,1934,1935,0,0,0]Ae[4761,5,1935,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Al[4761,15,4762,1918,4804,0,0]>Al[4761,15,4762,1537,3161,0,0]=5454Al[4761,15,4762,2459,4286,0,0]Ah[3974,5,3725,2507,0,0,0]Ah[4761,5,3726,2507,0,0,0]Ah[4761,5,4762,1918,0,0,0]98Ah[4761,6,4762,1083,0,0,0]:9Ah[3974,5,3975,1853,0,0,0]Ah[4761,5,4762,1853,0,0,0]Ae[4761,5,1851,0,0,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]Al[1058,14,1059,1059,3137,0,0]Ae[4761,2,4762,0,0,0,0]Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Al[3974,12,3975,2156,4388,0,0]Ab[4331,3,0,0,0,0,0]Ak[1374,14,1375,1375,129,0,0]Al[1374,14,1551,1551,4037,0,0]98Al[3974,13,3975,2485,3353,0,0]Al[4761,13,4762,2459,3354,0,0]Ai[3974,13,3975,0,4829,0,0]Al[4761,13,4762,3308,4830,0,0];=Al[3974,13,3975,4110,4906,0,0]Al[4761,13,4762,2164,3351,0,0]Al[3974,13,3975,3730,3323,0,0]Al[3974,13,3975,3730,3325,0,0]Al[3974,13,3975,2485,2417,0,0]Ak[3974,13,3975,1057,482,0,0]Al[4761,13,4762,2459,2418,0,0]<;Ai[3974,13,3975,0,1613,0,0]Ai[3974,13,3975,0,1596,0,0]Al[4761,13,4762,1971,1614,0,0]Al[4761,13,4762,1537,1597,0,0]20Ae[1374,7,1551,0,0,0,0]?>Al[1374,14,1551,1551,1370,0,0]Ao[1058,13,1059,1059,4899,3170,0]Ai[3974,15,3975,0,3145,0,0]Ah[3974,5,3975,1527,0,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1527,0,0,0]Ah[3974,6,3975,3731,0,0,0]Ah[4761,6,4762,3731,0,0,0]Al[4761,15,4762,2459,4286,0,0]Ah[4761,5,4762,1945,0,0,0]Ah[3974,5,3975,1552,0,0,0]Ah[4761,5,4762,1552,0,0,0]97Ae[1058,5,1553,0,0,0,0]:8Ah[4761,6,4762,1918,0,0,0]Ah[3974,5,3975,1079,0,0,0]Ah[4761,5,4762,1079,0,0,0]Ah[3974,5,3975,1563,0,0,0]Ah[4761,5,4762,1563,0,0,0]Ah[3974,5,3975,3308,0,0,0]Ah[4761,5,4762,3308,0,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,3731,4520,0,0]Ah[3974,5,3975,3730,0,0,0]Al[3974,15,3975,4110,3165,0,0]Al[3974,15,3975,4110,3352,0,0]Al[3974,15,3975,4115,3370,0,0]Al[3974,15,3975,4115,4195,0,0]An[1374,13,1375,1375,129,4285,0]Ao[1374,13,1375,1375,4006,4285,0]Ao[3974,13,3975,3730,3325,4285,0]Ao[3974,13,3975,3730,4967,4285,0]Ao[3974,13,3975,3730,4805,4285,0]Ao[3974,13,3975,2458,2413,4285,0]Ao[3974,13,3975,2458,4288,4285,0]An[3974,13,3975,1057,482,4285,0]Ao[3974,13,3975,1057,4431,4285,0]Ao[3974,13,3975,4110,4424,4285,0]Ao[3974,13,3975,4110,3165,4285,0]Ao[3974,13,3975,4110,3352,4285,0]Ao[3974,13,3975,4110,4423,4285,0]Ao[3974,13,3975,4115,3370,4285,0]Ao[3974,13,3975,4115,4195,4285,0]Al[3974,13,3975,0,1613,4285,0]Al[3974,13,3975,0,4964,4285,0]Al[3974,13,3975,0,1596,4285,0]Al[3974,13,3975,0,2448,4285,0]Al[3974,13,3975,0,3331,4285,0]Al[3974,13,3975,0,3162,4285,0]Al[3974,13,3975,0,4523,4285,0]Al[3974,13,3975,0,2030,4285,0]Al[3974,13,3975,0,4147,4285,0]Al[3974,13,3975,0,4519,4285,0]Al[3974,13,3975,0,4540,4285,0]Al[3974,13,3975,0,4829,4285,0]Al[3974,13,3975,0,4844,4285,0]Al[3974,13,3975,0,4019,4285,0]Al[3974,13,3975,0,4400,4285,0]Al[3974,13,3975,0,4189,4285,0]Al[3974,13,3975,0,5021,4285,0]Al[3974,13,3975,0,4141,4285,0]Al[3974,13,3975,0,4009,4285,0]Al[3974,13,3975,0,3145,4285,0]Al[3974,13,3975,0,4516,4285,0]Al[3974,13,3975,0,4706,4285,0]Al[3974,13,3975,0,4153,4285,0]Al[3974,13,3975,0,4156,4285,0]Al[3974,13,3975,0,4161,4285,0]Al[3974,13,3975,0,4863,4285,0]Al[3974,13,3975,0,3334,4285,0]Ak[3974,13,3975,0,489,4285,0]Ak[3974,13,3975,0,121,4285,0]Al[3974,13,3975,0,2436,4285,0]Ak[3974,13,3725,0,115,4285,0]Al[3974,13,3725,0,3134,4285,0]Al[3974,13,3725,0,1337,4285,0]Al[3974,13,3725,0,4014,4285,0]Al[3974,13,3725,0,2003,4285,0]Al[3974,13,3725,0,4307,4285,0]Al[3974,13,1934,0,4960,4285,0]Al[3974,13,1934,0,1350,4285,0]Ao[4761,13,4762,1971,1614,4285,0]Ao[4761,13,4762,1971,4965,4285,0]Ao[4761,13,4762,3190,4806,4285,0]Ao[4761,13,4762,1537,1597,4285,0]Ao[4761,13,4762,1537,4185,4285,0]Ao[4761,13,4762,1537,3346,4285,0]Ao[4761,13,4762,1537,3161,4285,0]Ao[4761,13,4762,1537,4860,4285,0]Ao[4761,13,4762,1537,3321,4285,0]Ao[4761,13,4762,1552,2449,4285,0]Ao[4761,13,4762,1552,3332,4285,0]Ao[4761,13,4762,1552,3163,4285,0]Ao[4761,13,4762,1552,4524,4285,0]Ao[4761,13,4762,1083,3166,4285,0]Ao[4761,13,4762,1083,4315,4285,0]Ao[4761,13,4762,1088,2031,4285,0]Ao[4761,13,4762,1088,3371,4285,0]Ao[4761,13,4762,1088,4018,4285,0]Ao[4761,13,4762,2164,4425,4285,0]Ao[4761,13,4762,2164,4151,4285,0]Ao[4761,13,4762,3731,4520,4285,0]Ao[4761,13,4762,3731,3158,4285,0]Ao[4761,13,4762,3731,4391,4285,0]Ao[4761,13,4762,3731,5050,4285,0]Ao[4761,13,4762,3308,4541,4285,0]Ao[4761,13,4762,3308,4830,4285,0]Ao[4761,13,4762,3308,4845,4285,0]Ao[4761,13,4762,3308,4020,4285,0]Ao[4761,13,4762,1853,4401,4285,0]Ao[4761,13,4762,1853,4191,4285,0]Ao[4761,13,4762,1853,5022,4285,0]Ao[4761,13,4762,1853,4143,4285,0]Ao[4761,13,4762,1853,4010,4285,0]Ao[4761,13,4762,1527,3146,4285,0]Ao[4761,13,4762,1527,3338,4285,0]Ao[4761,13,4762,1527,3360,4285,0]Ao[4761,13,4762,1527,4158,4285,0]Ao[4761,13,4762,1527,4518,4285,0]Ao[4761,13,4762,1527,4708,4285,0]Ao[4761,13,4762,1527,2407,4285,0]Ao[4761,13,4762,2459,4712,4285,0]Ao[4761,13,4762,2459,4154,4285,0]Ao[4761,13,4762,1918,4532,4285,0]Ao[4761,13,4762,1918,5053,4285,0]Ao[4761,13,4762,1918,2420,4285,0]Ao[4761,13,4762,1918,4804,4285,0]Ao[4761,13,4762,1945,4157,4285,0]Ao[4761,13,4762,1945,4162,4285,0]Al[4761,13,4762,0,4429,4285,0]Al[4761,13,4762,0,4864,4285,0]Ao[4761,13,4762,4328,3335,4285,0]Ao[4761,13,4762,4328,5146,4285,0]An[4761,13,4762,4762,490,4285,0]An[4761,13,4762,4762,122,4285,0]Ao[4761,13,4762,4762,2437,4285,0]Ao[4761,13,4762,4762,3144,4285,0]Ao[4761,13,1851,1851,4321,4285,0]Ao[4761,13,5006,5006,4905,4285,0]Ao[4761,13,5006,5006,1352,4285,0]Ao[4761,13,1935,1935,4961,4285,0]Al[4761,13,1935,0,1351,4285,0]Am[4761,13,3726,137,116,4285,0]An[4761,13,3726,137,3135,4285,0]Ao[4761,13,3726,1522,1338,4285,0]Ao[4761,13,3726,1522,4015,4285,0]Ao[4761,13,3726,2507,2004,4285,0]Ao[4761,13,3726,2507,4308,4285,0]Ao[4761,13,2135,2135,1356,4285,0]Ao[4465,13,4466,4466,2434,4285,0]Ao[4465,13,4466,4466,2447,4285,0]Ao[4465,13,4466,4466,4163,4285,0]Ao[1058,13,1059,1059,4899,4285,0]Ao[1058,13,1553,1553,4528,4285,0]Ah[3974,7,3975,2156,0,0,0]Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Ai[3974,12,1855,0,3155,0,0]Al[4761,12,1856,1856,3156,0,0]Al[4465,14,4466,4466,2434,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]10Ae[1058,7,1059,0,0,0,0]Al[3974,13,3975,2458,2413,0,0]Ae[4331,7,4332,0,0,0,0]Ae[1374,7,1375,0,0,0,0]Al[4761,13,4762,3731,3326,0,0]Al[3974,13,3975,3730,3325,0,0]1Al[4761,13,4762,2164,4425,0,0]Al[1374,13,1551,1551,1370,0,0]Al[1374,14,1551,1551,4037,0,0]Al[3974,12,3975,2156,2015,0,0]Ao[3974,13,3975,1564,4196,2015,0]Ao[3974,13,3975,1100,4032,2015,0]Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Ah[3974,5,3975,1057,0,0,0]Ai[3974,15,3975,0,3145,0,0]Ah[3974,5,3975,1527,0,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1527,0,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,3731,4520,0,0]Ah[4761,5,4762,2459,0,0,0]Al[4761,15,4762,3731,1355,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,2459,4154,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]32Al[4761,15,4762,3731,3158,0,0]><><><21Ah[3974,5,1934,1935,0,0,0]Ae[4761,5,1935,0,0,0,0]10Ah[3974,5,3975,4895,0,0,0]Ah[4761,5,4762,4895,0,0,0]Al[4761,15,2135,2135,1353,0,0]5Al[4761,15,4762,1918,4532,0,0]Ai[3974,12,3975,0,2441,0,0]Al[3974,13,3975,0,4279,2441,0]Al[3974,13,3975,0,4173,2441,0]Al[4761,12,4762,1563,2442,0,0]Ao[4761,13,4762,4550,4280,2442,0]Ao[4761,13,4762,1563,4174,2442,0]Ae[4043,7,4044,0,0,0,0]Al[3974,13,3975,3730,3325,0,0]Al[4761,13,4762,3731,3326,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]32An[1374,13,1375,1375,129,4402,0]Ao[1058,13,1059,1059,3137,4402,0]An[1058,13,1845,1845,130,4402,0]An[1058,13,1940,1940,131,4402,0]An[3994,13,3995,3995,120,4402,0]Ao[3994,13,3995,3995,2404,4402,0]Ab[4761,3,0,0,0,0,0]Ak[4465,13,4466,397,4430,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0];:Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,2459,2414,0,0]?>32Ae[4331,7,4332,0,0,0,0]5Al[1058,14,1059,1059,3137,0,0]60Ae[1374,7,1375,0,0,0,0]Ai[3974,13,3975,0,4189,0,0]Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4191,0,0]Al[4761,13,4762,1853,4143,0,0]Ai[3974,14,3975,0,4189,0,0]Al[4761,14,4762,1853,4191,0,0]53Al[3974,13,3975,4115,3356,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ai[3974,15,3975,0,4519,0,0]Al[4761,15,4762,3731,4520,0,0]Ai[3974,10,3725,3726,0,0,0]Af[4761,10,3726,0,0,0,0]Ah[3974,5,3725,3726,0,0,0]Ae[4761,5,3726,0,0,0,0]Ai[3974,10,1855,1856,0,0,0]Af[4761,10,1856,0,0,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,2459,4154,0,0]Ai[3974,10,3975,3731,0,0,0]Ai[4761,10,4762,3731,0,0,0]Ah[4761,6,4762,1918,0,0,0]Ah[3974,6,3975,3730,0,0,0]Ah[4761,6,4762,3190,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]10Al[3974,15,3975,3730,4805,0,0]Al[4761,15,4762,3190,4806,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1552,3163,0,0]Ai[3974,14,3725,0,4795,0,0]Al[4761,14,3726,3726,4796,0,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]10Al[4761,13,4762,3190,3140,0,0]Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,2459,2414,0,0]Ab[4824,3,0,0,0,0,0]Ao[1058,13,1059,1059,4899,3170,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ah[3974,5,3975,3308,0,0,0]Ah[4761,5,4762,3308,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]54Ai[4761,10,4762,1918,0,0,0]Al[4761,15,4762,3731,1355,0,0]7600Al[4761,15,4762,1083,4315,0,0]4365Al[4761,15,2135,2135,1353,0,0]Ae[1058,9,1059,0,0,0,0]:9Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]<;<;Ah[4761,6,4762,1537,0,0,0]21Ae[3974,5,3975,0,0,0,0]Ae[4761,5,4762,0,0,0,0]Ai[3974,15,3975,0,4153,0,0]Ah[3974,5,1855,1856,0,0,0]Al[4761,15,4762,2459,4154,0,0]Al[4761,15,4762,1918,4804,0,0]Ae[4761,5,1856,0,0,0,0]An[1374,13,1375,1375,129,4392,0]Ao[1058,13,1059,1059,3137,4392,0]An[1058,13,1845,1845,130,4392,0]An[1058,13,1940,1940,131,4392,0]An[3994,13,3995,3995,120,4392,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Ai[3974,13,3975,0,1613,0,0]Al[4761,13,4762,1971,1614,0,0]Al[3974,14,3975,2458,4288,0,0]Al[3974,13,3975,3730,4967,0,0]Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Ai[3974,13,3975,0,4279,0,0]Al[4761,13,4762,4550,4280,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]7Al[4761,13,4762,3731,3326,0,0]Al[3974,13,3975,3730,3325,0,0]0176Ao[4043,13,4044,4044,5054,4858,0]Ai[3974,13,3975,0,4533,0,0]Al[4761,13,4762,1079,4534,0,0]Ae[1058,6,1059,0,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]10Al[4761,15,4762,3731,3158,0,0]Ae[4761,5,5006,0,0,0,0]Ah[3974,5,3975,4110,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Al[4761,15,2135,2135,1353,0,0]Al[3974,14,3975,2458,4288,0,0]Ak[3974,13,3975,0,489,3128,0]Ak[3974,13,3975,0,121,3128,0]An[4761,13,4762,4762,490,3128,0]An[4761,13,4762,4762,122,3128,0]Ao[3994,13,3995,3995,2404,3361,0]Ai[3974,13,3975,0,4844,0,0]Al[4761,13,4762,3308,4845,0,0]Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,12,3975,0,4801,0,0]Al[4761,12,4762,3731,4802,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Ai[3974,14,3975,0,3162,0,0]Al[4761,14,4762,1552,3163,0,0]Ai[3974,12,3725,0,3327,0,0]Ak[3974,13,3725,0,115,3327,0]Al[3974,13,3725,0,1337,3327,0]Al[3974,13,3725,0,2003,3327,0]Al[4761,12,3726,3726,3328,0,0]Am[4761,13,3726,137,116,3328,0]Ao[4761,13,3726,1522,1338,3328,0]Ao[4761,13,3726,2507,2004,3328,0]Al[3974,14,3975,2458,4288,0,0]Al[4761,13,4762,2164,2024,0,0]Ah[4761,2,4326,4114,0,0,0]Ae[1374,7,1375,0,0,0,0]==Ak[3974,13,3975,1057,482,0,0]Ak[4465,13,4466,397,4430,0,0]032Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Ae[4761,2,4762,0,0,0,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Ai[3974,10,3975,4328,0,0,0]Ai[4761,10,4762,4328,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]Ai[3974,15,3975,0,4153,0,0]Al[4761,15,4762,2459,4154,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ah[3974,5,3975,1853,0,0,0]Ah[4761,5,4762,1853,0,0,0]Ai[3974,12,1855,0,3155,0,0]Al[4761,12,1856,1856,3156,0,0]Al[3974,13,3975,2458,2413,0,0]Al[4761,13,4762,3731,3326,0,0]Al[4761,13,4762,2459,2414,0,0]Al[4761,13,4762,4762,3144,0,0]Ai[3974,13,3725,0,4307,0,0]Al[4761,13,3726,2507,4308,0,0]Al[3974,13,3975,2156,3168,0,0]0Al[3974,13,3975,3730,3325,0,0]6Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Al[1058,14,1059,1059,3137,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Al[4761,15,2135,2135,1353,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]10Al[4761,15,4762,1537,4860,0,0]Al[4761,15,4762,3731,3158,0,0]Al[4761,15,4762,3731,4391,0,0]Ah[4761,5,4762,3731,0,0,0]Ai[3974,15,3975,0,3145,0,0]Al[4761,15,4762,1527,3146,0,0]Ah[4761,5,4762,1918,0,0,0]Ae[4043,5,4044,0,0,0,0]Al[1058,13,1059,1059,3137,0,0]Al[3974,13,3975,3730,3325,0,0]Ai[3974,12,1855,0,2016,0,0]Al[3974,13,1934,0,3365,2016,0]Al[4761,12,1856,1856,2017,0,0]Ao[4761,13,1935,1935,3366,2017,0]Ah[4761,7,4940,4939,0,0,0]0Al[4761,13,4762,3731,3326,0,0]6Ak[3974,13,3975,1057,482,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]Ao[4043,13,4044,4044,5054,4858,0]0Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4143,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]10Ai[3974,13,3975,0,4829,0,0]Al[4761,13,4762,3308,4830,0,0]Am[1374,13,1375,1375,129,464,0]An[1058,13,1059,1059,3137,464,0]Am[1058,13,1845,1845,130,464,0]Am[1058,13,1940,1940,131,464,0]Am[3994,13,3995,3995,120,464,0]??>An[1374,13,1375,1375,129,4402,0]Ao[1058,13,1059,1059,3137,4402,0]An[1058,13,1845,1845,130,4402,0]An[1058,13,1940,1940,131,4402,0]An[3994,13,3995,3995,120,4402,0]Ao[3994,13,3995,3995,2404,4402,0]Ai[3974,13,3975,0,4189,0,0]Al[4761,13,4762,1853,4191,0,0]Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]Ae[1058,5,4248,0,0,0,0]0Ai[3974,13,3975,0,4400,0,0]Al[4761,13,4762,1853,4401,0,0]Ai[3974,13,3975,0,4141,0,0]Al[4761,13,4762,1853,4143,0,0]Al[3974,13,3975,2458,2413,0,0]Al[3974,13,3975,2485,2417,0,0]Al[4761,13,4762,2459,2418,0,0]Al[4761,13,4762,3731,3326,0,0]Ao[4043,13,4044,4044,5054,4858,0];:Ah[3974,5,1855,1856,0,0,0]Ae[4761,5,1856,0,0,0,0]Ai[3974,15,1934,0,1350,0,0]Ai[4761,15,1935,0,1351,0,0]?>?>Al[3974,13,3975,3730,3325,0,0]6Ai[3974,13,3975,0,4540,0,0]Al[4761,13,4762,3308,4541,0,0]Ae[4043,7,4044,0,0,0,0]Ae[4331,7,4332,0,0,0,0]Ai[3974,14,3975,0,3331,0,0]Al[4761,14,4762,1552,3332,0,0]?=Ai[3974,19,3975,0,4863,0,0]Ai[4761,19,4762,0,4864,0,0]2=<==<>=>=>=<;<;Al[4761,14,5006,5006,4905,0,0]?>Ai[4761,18,4762,3308,0,0,0]") \ No newline at end of file diff --git a/pr/436/docs/search.index/function/96c428e3eee0.js b/pr/436/docs/search.index/function/3691ebe673bb.js similarity index 97% rename from pr/436/docs/search.index/function/96c428e3eee0.js rename to pr/436/docs/search.index/function/3691ebe673bb.js index ebb1af88e..3ad12fd74 100644 --- a/pr/436/docs/search.index/function/96c428e3eee0.js +++ b/pr/436/docs/search.index/function/3691ebe673bb.js @@ -1 +1 @@ -rd_("Bf[\"{{{AICl{AODl}}{AICl{AODl}}}CNf}\",[]]Bf[\"{{{AICl{BEHj}}{AICl{BEHj}}}CNf}\",[]]Bd[\"{{{AICl{CLd}}{AICl{CLd}}}CNf}\",[]]Bf[\"{{{AICl{BBIn}}{AICl{BBIn}}}CNf}\",[]]Bf[\"{{{AICl{BBI`}}{AICl{BBI`}}}CNf}\",[]]Bf[\"{{{AICl{AHKj}}{AICl{AHKj}}}CNf}\",[]]Bf[\"{{{AICl{AJC`}}{AICl{AJC`}}}CNf}\",[]]Bf[\"{{{AICl{BBHn}}{AICl{BBHn}}}CNf}\",[]]Bf[\"{{{AICl{AJEd}}{AICl{AJEd}}}CNf}\",[]]Bf[\"{{{AICl{B@Lf}}{AICl{B@Lf}}}CNf}\",[]]Bd[\"{{{AICl{LIj}}{AICl{LIj}}}CNf}\",[]]Bf[\"{{{AICl{BFLh}}{AICl{BFLh}}}CNf}\",[]]Bd[\"{{{AICl{JFj}}{AICl{JFj}}}CNf}\",[]]Bd[\"{{{AICl{LGh}}{AICl{LGh}}}CNf}\",[]]Bf[\"{{{AICl{ACB`}}{AICl{ACB`}}}CNf}\",[]]Bf[\"{{{AICl{AHKd}}{AICl{AHKd}}}CNf}\",[]]Bd[\"{{{AICl{OMl}}{AICl{OMl}}}CNf}\",[]]Bf[\"{{{AICl{B@Ff}}{AICl{B@Ff}}}CNf}\",[]]Bf[\"{{{AICl{BCDn}}{AICl{BCDn}}}CNf}\",[]]Bf[\"{{{AICl{AOFf}}{AICl{AOFf}}}CNf}\",[]]Bf[\"{{{AICl{AOEb}}{AICl{AOEb}}}CNf}\",[]]Bf[\"{{{AICl{AHIb}}{AICl{AHIb}}}CNf}\",[]]Bf[\"{{{AICl{BCDh}}{AICl{BCDh}}}CNf}\",[]]Bf[\"{{{AICl{BDLd}}{AICl{BDLd}}}CNf}\",[]]Bf[\"{{{AICl{B@Gb}}{AICl{B@Gb}}}CNf}\",[]]Bf[\"{{{AICl{B@Gh}}{AICl{B@Gh}}}CNf}\",[]]Bf[\"{{{AICl{B@Hb}}{AICl{B@Hb}}}CNf}\",[]]Bf[\"{{{AICl{BEOn}}{AICl{BEOn}}}CNf}\",[]]Bd[\"{{{AICl{CMb}}{AICl{CMb}}}CNf}\",[]]Bb[\"{{{AICl{Ob}}{AICl{Ob}}}CNf}\",[]]Bf[\"{{{AICl{AC@h}}{AICl{AC@h}}}CNf}\",[]]Bf[\"{{{AICl{BGO`}}{AICl{BGO`}}}CNf}\",[]]Bd[\"{{{AICl{JHl}}{AICl{JHl}}}CNf}\",[]]Bd[\"{{{AICl{LIl}}{AICl{LIl}}}CNf}\",[]]Bf[\"{{{AICl{BFLj}}{AICl{BFLj}}}CNf}\",[]]Bd[\"{{{AICl{JFl}}{AICl{JFl}}}CNf}\",[]]Bf[\"{{{AICl{BEHl}}{AICl{BEHl}}}CNf}\",[]]Bd[\"{{{AICl{LGj}}{AICl{LGj}}}CNf}\",[]]Bf[\"{{{AICl{AJBd}}{AICl{AJBd}}}CNf}\",[]]Bf[\"{{{AICl{AHKb}}{AICl{AHKb}}}CNf}\",[]]Bf[\"{{{AICl{BEOh}}{AICl{BEOh}}}CNf}\",[]]Bf[\"{{{AICl{AIOb}}{AICl{AIOb}}}CNf}\",[]]Bf[\"{{{AICl{ACBb}}{AICl{ACBb}}}CNf}\",[]]Bf[\"{{{AICl{AHKf}}{AICl{AHKf}}}CNf}\",[]]Bf[\"{{{AICl{AHKl}}{AICl{AHKl}}}CNf}\",[]]Bf[\"{{{AICl{BAKf}}{AICl{BAKf}}}CNf}\",[]]Bd[\"{{{AICl{OMn}}{AICl{OMn}}}CNf}\",[]]Bf[\"{{{AICl{AJEf}}{AICl{AJEf}}}CNf}\",[]]Bf[\"{{{AICl{AOFd}}{AICl{AOFd}}}CNf}\",[]]Bf[\"{{{AICl{AOHh}}{AICl{AOHh}}}CNf}\",[]]Bf[\"{{{AICl{BBIb}}{AICl{BBIb}}}CNf}\",[]]Bf[\"{{{AICl{B@Fn}}{AICl{B@Fn}}}CNf}\",[]]Bf[\"{{{AICl{BCE`}}{AICl{BCE`}}}CNf}\",[]]Bf[\"{{{AICl{AHJl}}{AICl{AHJl}}}CNf}\",[]]Bf[\"{{{AICl{BBDn}}{AICl{BBDn}}}CNf}\",[]]Bf[\"{{{AICl{AOFh}}{AICl{AOFh}}}CNf}\",[]]Bf[\"{{{AICl{AOEd}}{AICl{AOEd}}}CNf}\",[]]Bf[\"{{{AICl{AHId}}{AICl{AHId}}}CNf}\",[]]Bf[\"{{{AICl{BBFl}}{AICl{BBFl}}}CNf}\",[]]Bf[\"{{{AICl{AJAd}}{AICl{AJAd}}}CNf}\",[]]Bf[\"{{{AICl{AJD`}}{AICl{AJD`}}}CNf}\",[]]Bf[\"{{{AICl{B@Gl}}{AICl{B@Gl}}}CNf}\",[]]Bf[\"{{{AICl{BCDl}}{AICl{BCDl}}}CNf}\",[]]Bf[\"{{{AICl{BDLh}}{AICl{BDLh}}}CNf}\",[]]Bf[\"{{{AICl{BDM`}}{AICl{BDM`}}}CNf}\",[]]Bf[\"{{{AICl{B@Gd}}{AICl{B@Gd}}}CNf}\",[]]Bf[\"{{{AICl{ABNh}}{AICl{ABNh}}}CNf}\",[]]Bf[\"{{{AICl{BEHh}}{AICl{BEHh}}}CNf}\",[]]Bf[\"{{{AICl{B@Gj}}{AICl{B@Gj}}}CNf}\",[]]Bf[\"{{{AICl{B@Hd}}{AICl{B@Hd}}}CNf}\",[]]Bf[\"{{{AICl{BBIj}}{AICl{BBIj}}}CNf}\",[]]Bf[\"{{{AICl{BF@`}}{AICl{BF@`}}}CNf}\",[]]Bd[\"{{{AICl{CMd}}{AICl{CMd}}}CNf}\",[]]Bb[\"{{{AICl{Od}}{AICl{Od}}}CNf}\",[]]Bf[\"{{{AICl{AC@j}}{AICl{AC@j}}}CNf}\",[]]Bf[\"{{{AICl{AHI`}}{AICl{AHI`}}}CNf}\",[]]Bf[\"{{{AICl{BALb}}{AICl{BALb}}}CNf}\",[]]Bf[\"{{{AICl{BFEb}}{AICl{BFEb}}}CNf}\",[]]Bd[\"{{{AICl{JI`}}{AICl{JI`}}}CNf}\",[]]Bf[\"{{{AICl{BGOb}}{AICl{BGOb}}}CNf}\",[]]Bd[\"{{{AICl{JHn}}{AICl{JHn}}}CNf}\",[]]Bf[\"{{{AICl{B@Hf}}{AICl{B@Hf}}}CNf}\",[]]Ai[\"{{{AICl{CLd}}}LGh}\",[]]Aj[\"{{{AICl{BBIn}}}LGh}\",[]]Ak[\"{{{AICl{AJBl}}}AC@h}\",[]]Ak[\"{{{AICl{BFEd}}}AC@h}\",[]]Ak[\"{{{AICl{AJCh}}}AC@h}\",[]]o[\"{LFlLGj}\",[]]A`[\"{AHFjLGj}\",[]]A`[\"{BAIdLGj}\",[]]A`[\"{B@JlLGj}\",[]]A`[\"{B@InLGj}\",[]]Aa[\"{ABOlAC@j}\",[]]Aa[\"{ACBfAC@j}\",[]]Aa[\"{ABMnAC@j}\",[]]A`[\"{OLdAC@j}\",[]]n[\"{{}OKl}\",[]]A`[\"{CKlABNh}\",[]]Be[\"{{{AICl{LIj}}{AICl{LIj}}}ABOj}\",[]]Be[\"{{{AICl{LGh}}{AICl{LGh}}}ABOj}\",[]]Bg[\"{{{AICl{AOFf}}{AICl{AOFf}}}ABOj}\",[]]Bg[\"{{{AICl{B@Gh}}{AICl{B@Gh}}}ABOj}\",[]]Be[\"{{{AICl{CMb}}{AICl{CMb}}}ABOj}\",[]]Bc[\"{{{AICl{Ob}}{AICl{Ob}}}ABOj}\",[]]Bg[\"{{{AICl{AC@h}}{AICl{AC@h}}}ABOj}\",[]]Be[\"{{{AICl{LIl}}{AICl{LIl}}}ABOj}\",[]]Bg[\"{{{AICl{BEHl}}{AICl{BEHl}}}ABOj}\",[]]Be[\"{{{AICl{LGj}}{AICl{LGj}}}ABOj}\",[]]Bg[\"{{{AICl{AHKl}}{AICl{AHKl}}}ABOj}\",[]]Bg[\"{{{AICl{BAKf}}{AICl{BAKf}}}ABOj}\",[]]Bg[\"{{{AICl{AJEf}}{AICl{AJEf}}}ABOj}\",[]]Bg[\"{{{AICl{AOFh}}{AICl{AOFh}}}ABOj}\",[]]Bg[\"{{{AICl{BDM`}}{AICl{BDM`}}}ABOj}\",[]]Bg[\"{{{AICl{BEHh}}{AICl{BEHh}}}ABOj}\",[]]Bg[\"{{{AICl{B@Gj}}{AICl{B@Gj}}}ABOj}\",[]]Be[\"{{{AICl{CMd}}{AICl{CMd}}}ABOj}\",[]]Bc[\"{{{AICl{Od}}{AICl{Od}}}ABOj}\",[]]Bg[\"{{{AICl{AC@j}}{AICl{AC@j}}}ABOj}\",[]]o[\"{AC@hOb}\",[]]o[\"{AC@jOd}\",[]]n[\"{LFnOd}\",[]]o[\"{AHGfOd}\",[]]A`[\"{ACAjLGd}\",[]]A`[\"{ACAlLGd}\",[]]A`[\"{AC@dLGd}\",[]]A`[\"{ACAnLGd}\",[]]Bh[\"{{{AICl{A@b}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AODl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBId}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BGGl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AIOf}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AIOj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BFLn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEHj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ABMj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BAH`}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABNb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJCb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBFj}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{CLd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJBl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BFEd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBI`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AHKj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJC`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBHn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Lh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOH`}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJCh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJEd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Lf}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{OLh}}{AICl{CBnAHHn}}}LGl}\",[]]Cd[\"{{{AICl{LIj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BFLh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Cd[\"{{{AICl{JFj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{LGh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{ACB`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AJ@f}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AHKd}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BCEf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{OMl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{B@Ff}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BCDn}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BCFj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BCGh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEKj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEMh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOFf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BBF`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{B@Kj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BGCj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{B@Ej}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOEb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{AHIb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BCDh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BDLd}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{B@Gb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{B@Gh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{B@Hb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEOn}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{AJ@l}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BE@b}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{CMb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cc[\"{{{AICl{Ob}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{AC@h}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{ACAj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cc[\"{{{AICl{Nf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AHGl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{JGb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOEl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{OJf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BAJf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEGf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BGO`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOEh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BF@f}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BFL`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Cd[\"{{{AICl{JHl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Bi[\"{{{AICl{AIOl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABMl}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{LIl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BFLj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{JFl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEHl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{LGj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Kb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJBd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHKb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEOh}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AIOb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ACBb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJ@h}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHKf}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCEh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHKl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BAKf}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{OMn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJEf}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AOFd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AOHh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Fn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BCE`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AHJl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBDn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BGGd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{JIf}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCFl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCGj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEKl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEMj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOFh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBFb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Kn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BGCl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@En}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOEd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AHId}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBFl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJAd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJD`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Gl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BCDl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BDLh}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ABLn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BDM`}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABNd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Gd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AODh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJCd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BAJl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCFh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BGGj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABNh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEHh}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BCEd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BAGn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Gj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Hd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BF@`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJ@n}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BHCd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BE@d}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{CMd}}{AICl{CBnAHHn}}}LGl}\",[]]Bg[\"{{{AICl{Od}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AC@j}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ACAl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHI`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BALb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BFEb}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{JI`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BGOb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOEj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BF@l}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BFLb}}{AICl{CBnAHHn}}}LGl}\",[]]0Cd[\"{{{AICl{JHn}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Bg[\"{{{AICl{Nh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHGn}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{JGd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOEn}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{OJh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BAJh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEGh}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{JIb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{JIh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AC@d}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ACAn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Hf}}{AICl{CBnAHHn}}}LGl}\",[]]Ak[\"{{{AICl{B@Kb}}}ABMf}\",[]]A`[\"{ACB`CIl}\",[]]A`[\"{ACBbCIl}\",[]]Aj[\"{{{AICl{BAJl}}}LCl}\",[]]A`[\"{BGGjCIl}\",[]]o[\"{CKlCIl}\",[]]A`[\"{AC@dLCl}\",[]]Ce[\"{{{AICl{CBnBGCj}}{Nd{ACAb}}}{{AICl{CBnBGCj}}}}\",[]]Ce[\"{{{AICl{CBnBGCl}}{Nd{ACAd}}}{{AICl{CBnBGCl}}}}\",[]]8Af[\"{{ABMfCIl}BBId}\",[]]Ag[\"{{}{{Nd{BGGl}}}}\",[]]Ae[\"{{LClCIj}BAFn}\",[]]Cd[\"{{BBF`{LGd{B@Kj}}AHLb{Nd{OKn}}}{{LH`{ABMj}}}}\",[]]Ah[\"{{{Nl{JKj}}}AJBf}\",[]]A`[\"{OKfBCFj}\",[]]Ah[\"{{{Nd{OId}}}BBF`}\",[]]Bc[\"{{{Nd{B@Kl}}{Nd{BEGj}}}B@Kj}\",[]]Ai[\"{{{Nd{B@El}}}B@Ej}\",[]]B`[\"{{{AICl{{LAf{Ml}}}}}B@Gh}\",[]]Ae[\"{{CIjLCl}BE@b}\",[]]Ag[\"{{CMbObCIl}AC@h}\",[]]Aj[\"{{{Nd{AHGl}}CIh}Nf}\",[]]An[\"{{{Nd{AOEl}}OIjCIh}JGb}\",[]]An[\"{{{Nd{BAJf}}OIjCIh}OJf}\",[]]Ae[\"{{LClCIj}BAG`}\",[]]Ah[\"{{{Nl{JKj}}}AJBh}\",[]]Bm[\"{{{Nd{BBFb}}{LGd{{Nd{B@Kn}}}}CNf}ABMl}\",[]]A`[\"{LClBHFf}\",[]]o[\"{{}BHBj}\",[]]A`[\"{OKfBCFl}\",[]]Ah[\"{{{Nd{OIf}}}BBFb}\",[]]Bc[\"{{{Nd{B@L`}}{Nd{BEGl}}}B@Kn}\",[]]Ai[\"{{{Nd{B@F`}}}B@En}\",[]]Ek[\"{{ABLd{AICl{c}}{AICl{{LAf{CIj}}}}CNf}{{LGn{{LCj{BAJl{LGd{ABLd}}}}BEHh}}}{BELfJKb}}\",[\"\"]]B`[\"{{{AICl{{LAf{Ml}}}}}B@Gj}\",[]]Af[\"{{BF@`LIf}BBIj}\",[]]A`[\"{LClBHCd}\",[]]Ae[\"{{CIjLCl}BE@d}\",[]]Ag[\"{{CMdOdCIl}AC@j}\",[]]Aj[\"{{{Nd{AHGn}}CIh}Nh}\",[]]An[\"{{{Nd{AOEn}}OIjCIh}JGd}\",[]]An[\"{{{Nd{BAJh}}OIjCIh}OJh}\",[]]Aj[\"{AJEb{{LH`{BBIl}}}}\",[]]Ai[\"{{{Nd{BDOl}}}BGMd}\",[]]Ai[\"{{{Nd{BE@`}}}BGMf}\",[]]o[\"{CMbCMb}\",[]]o[\"{CMdCMd}\",[]]Ak[\"{{{AICl{AJDf}}}AJDd}\",[]]Ai[\"{{{AICl{OKn}}}OIj}\",[]]0Aj[\"{{{AICl{B@Lh}}}OIj}\",[]]Ak[\"{{{AICl{BAKn}}}AJDd}\",[]]Ak[\"{{{AICl{AJDh}}}AJDd}\",[]]Ak[\"{{{AICl{BAL`}}}AJDd}\",[]]Bg[\"{{{AICl{AIOj}}LGh}{{LGd{ABMf}}}}\",[]]Aa[\"{AHKdABMf}\",[]]Bg[\"{{{AICl{AIOl}}LGj}{{LGd{ABMf}}}}\",[]]Aa[\"{AHKfABMf}\",[]]Ah[\"{A@d{{LHb{HK`}}}}\",[]]Ah[\"{A@f{{LHb{HK`}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Aa[\"{B@InAJCn}\",[]]Aa[\"{AC@dAJCn}\",[]]Aa[\"{BBIjBF@`}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]A`[\"{ABLnJFh}\",[]]Am[\"{{{AICl{CBnABOf}}}HK`}\",[]]Am[\"{{{AICl{CBnABNb}}}HK`}\",[]]Al[\"{{{AICl{CBnCLd}}}HK`}\",[]]Am[\"{{{AICl{CBnBBIn}}}HK`}\",[]]Am[\"{{{AICl{CBnAJBl}}}HK`}\",[]]Am[\"{{{AICl{CBnAJCh}}}HK`}\",[]]Al[\"{{{AICl{CBnLEd}}}HK`}\",[]]A`[\"{cc{}}\",[\"T\"]]000000000000000000000Aa[\"{BCDnBEHj}\",[]]1111111111Aa[\"{AHKjBBI`}\",[]]22Aa[\"{AJC`AHKj}\",[]]Aa[\"{BCDnAHKj}\",[]]Aa[\"{BCDnAJC`}\",[]]55Aa[\"{AHKjBBHn}\",[]]666Aa[\"{B@LfAJEd}\",[]]Aa[\"{BCDnAJEd}\",[]]Aa[\"{B@FfAJEd}\",[]]9Aa[\"{BCDnB@Lf}\",[]]:::::::n[\"{MlLIj}\",[]]A`[\"{AC@hLIj}\",[]]A`[\"{BEOnLIj}\",[]]=o[\"{CIhLIj}\",[]]o[\"{CIjLIj}\",[]]??Al[\"{cLGh{{CKf{CIj}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]0000000A`[\"{JG`BCDn}\",[]]Aa[\"{AOE`BCDn}\",[]]A`[\"{JHhBCDn}\",[]]3333A`[\"{LIjAOFf}\",[]]44444Aa[\"{BFLhAOEb}\",[]]5Aa[\"{BCGlAOEb}\",[]]66A`[\"{JHhBCDh}\",[]]7Aa[\"{BF@fB@Gb}\",[]]888A`[\"{LIjBEOn}\",[]]99999A`[\"{LIjAC@h}\",[]]::::::::::::Aa[\"{AOHnAOEh}\",[]];;;;;;;A`[\"{BCG`JHl}\",[]]A`[\"{BGNnJHl}\",[]]A`[\"{BH@`JHl}\",[]]A`[\"{AJAjJHl}\",[]]A`[\"{BBH`JHl}\",[]]A`[\"{BDOdJHl}\",[]]A`[\"{BBFnJHl}\",[]]A`[\"{BDLjJHl}\",[]]A`[\"{BDMlJHl}\",[]]A`[\"{BCEjJHl}\",[]]A`[\"{cc{}}\",[\"T\"]]A`[\"{BGJ`JHl}\",[]]1111111111111111n[\"{MlLIl}\",[]]o[\"{CIjLIl}\",[]]A`[\"{BF@`LIl}\",[]]o[\"{CIhLIl}\",[]]5A`[\"{AC@jLIl}\",[]]666Al[\"{cLGj{{CKf{CIj}}}}\",[\"T\"]]777777777777777777Aa[\"{BBIjBCE`}\",[]]88888A`[\"{AHKbJIf}\",[]]99999A`[\"{LIlAOFh}\",[]]::::Aa[\"{BFLjAOEd}\",[]]Aa[\"{BCGlAOEd}\",[]]<<<<<<>>>>>>Aa[\"{BALbBEHh}\",[]]??????A`[\"{JI`BBIj}\",[]]A`[\"{cc{}}\",[\"T\"]]A`[\"{LIlBF@`}\",[]]11111A`[\"{LIlAC@j}\",[]]222222A`[\"{BALbJI`}\",[]]333Aa[\"{AOHnAOEj}\",[]]4444444A`[\"{BDMlJHn}\",[]]A`[\"{BH@`JHn}\",[]]A`[\"{BDOdJHn}\",[]]A`[\"{BGJ`JHn}\",[]]A`[\"{BBH`JHn}\",[]]A`[\"{BBFnJHn}\",[]]A`[\"{BGNnJHn}\",[]]A`[\"{BDLjJHn}\",[]]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ce[\"{{{Nd{B@Ef}}}{{LGn{BDOlc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Ef{{LGn{BDOlc}}}{}}\",[\"TryFrom::Error\"]]Ce[\"{{{Nd{B@Kf}}}{{LGn{BE@`c}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Kf{{LGn{BE@`c}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{LGn{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000Aj[\"{{}{{LGn{c}}}{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{A@j{{AICl{CBnc}}}}{AICl{CBnOHh}}}CLl{}}\",[\"F\"]]000000000Cc[\"{{{AICl{BBIl}}AJEb{AICl{ACAn}}}{{LH`{HK`}}}}\",[]]Bk[\"{{{AICl{BGBh}}B@Gh}{{LGn{HK`AJ@l}}}}\",[]]Bk[\"{{{AICl{BGBj}}B@Gj}{{LGn{HK`AJ@n}}}}\",[]]0Bk[\"{{{AICl{BHBj}}B@Gj}{{LGn{HK`AJ@n}}}}\",[]]Ak[\"{{{AICl{AIOj}}}ABLb}\",[]]Bi[\"{{{AICl{CBnB@Lj}}}{{LGn{LIjJHj}}}}\",[]]Bi[\"{{{AICl{CBnB@Lj}}}{{LGn{CMbJHj}}}}\",[]]Bh[\"{{{AICl{CBnB@Lj}}}{{LGn{ObJHj}}}}\",[]]Bj[\"{{{AICl{CBnB@Lj}}}{{LGn{AC@hJHj}}}}\",[]]Bf[\"{{{AICl{CBnB@Lj}}}{{LHd{LIl}}}}\",[]]Bf[\"{{{AICl{CBnB@Lj}}}{{LHd{CMd}}}}\",[]]Be[\"{{{AICl{CBnB@Lj}}}{{LHd{Od}}}}\",[]]Bg[\"{{{AICl{CBnB@Lj}}}{{LHd{AC@j}}}}\",[]]Aa[\"{AODlBFLf}\",[]]Bc[\"{{{AICl{AIOn}}}{{Nl{AIOn}}}}\",[]]Ba[\"{{{AICl{Nf}}}{{Nl{AIOn}}}}\",[]]Bb[\"{{{AICl{JGb}}}{{Nl{AIOn}}}}\",[]]Bb[\"{{{AICl{OJf}}}{{Nl{AIOn}}}}\",[]]Bc[\"{{{AICl{AJ@`}}}{{Nl{AJ@`}}}}\",[]]Ba[\"{{{AICl{Nh}}}{{Nl{AJ@`}}}}\",[]]Bb[\"{{{AICl{JGd}}}{{Nl{AJ@`}}}}\",[]]Bb[\"{{{AICl{OJh}}}{{Nl{AJ@`}}}}\",[]]A`[\"{ACB`CIl}\",[]]An[\"{{{AICl{CBnAIOl}}}AHHh}\",[]]A`[\"{ACBbCIl}\",[]]C`[\"{{{AICl{CBnB@Gh}}}{{AICl{CBn{LAf{Ml}}}}}}\",[]]C`[\"{{{AICl{CBnB@Gj}}}{{AICl{CBn{LAf{Ml}}}}}}\",[]]Ah[\"{Ml{{LGd{B@Hb}}}}\",[]]Ah[\"{Ml{{LGd{B@Hd}}}}\",[]]Ah[\"{Ml{{LGd{B@Hf}}}}\",[]]A`[\"{AC@hCMb}\",[]]A`[\"{AC@jCMd}\",[]]Bi[\"{AIOf{{LGn{{LCj{AIOjBCHb}}AIOf}}}}\",[]]o[\"{CMbCNf}\",[]]o[\"{CMdCNf}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]21Aj[\"{{{AICl{ABNb}}}CNf}\",[]]Aj[\"{{{AICl{ABNd}}}CNf}\",[]]Bn[\"{{{AICl{CBnB@Kj}}CNf}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}CNf}{{AICl{CBnB@Kn}}}}\",[]]Bl[\"{{{AICl{OKn}}OIj}{{A@j{{Nl{AINd}}}}}}\",[]]Bm[\"{{{AICl{B@Lh}}OIj}{{A@j{{Nl{AINd}}}}}}\",[]]Bm[\"{{{AICl{AOH`}}OIj}{{A@j{{Nl{AINd}}}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Ak[\"{{{AICl{AIOj}}}ABOf}\",[]]Bc[\"{{{AICl{AIOj}}AJCnAJBb}ABOh}\",[]]Ce[\"{{{AICl{CBnAIOl}}AHI`AJBdOIj}{{LGn{LGjAHJl}}}}\",[]]43Bm[\"{{{AICl{CBnAIOl}}LGj}{{LGn{HK`AIOb}}}}\",[]]E`[\"{{{A@j{{AICl{CBnBDL`}}}}{AICl{CBnOHh}}}{{CLl{{LGd{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{A@j{{AICl{CBnAJBl}}}}{AICl{CBnOHh}}{AICl{CBnOKh}}}{{CLl{{LH`{HK`}}}}}}\",[]]Ef[\"{{{A@j{{AICl{CBnAJBl}}}}{AICl{CBnOHh}}{AICl{CBn{LAf{Ml}}}}}{{CLl{{LH`{LCl}}}}}}\",[]]Ec[\"{{{AICl{CBnAJBl}}{AICl{CBnOHh}}{AICl{CBn{LAf{Ml}}}}}{{CLl{{LGn{LClAHKj}}}}}}\",[]]Fg[\"{{{AICl{CBnBBDh}}{AICl{CBnOHh}}{AICl{CBn{LAf{AJA`}}}}{AICl{CBn{LAf{AC@d}}}}}{{CLl{{LH`{LCl}}}}}}\",[]]Dn[\"{{{A@j{{AICl{CBnAHL`}}}}{AICl{ACAn}}{AICl{CBnOHh}}}{{CLl{{LH`{HK`}}}}}}\",[]];:Cd[\"{{{AICl{CBnBBF`}}{Nd{OId}}}{{AICl{CBnBBF`}}}}\",[]]Cd[\"{{{AICl{CBnBBFb}}{Nd{OIf}}}{{AICl{CBnBBFb}}}}\",[]]Cj[\"{{{AICl{B@Kl}}CIjB@Gh{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{BDOn}}CIjB@Gh{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{B@L`}}CIjB@Gj{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{BE@`}}CIjB@Gj{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{BGMf}}CIjB@Gf{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Bb[\"{LCl{{LCj{LCl{LGd{LCl}}}}}}\",[]]000Aj[\"{{{AICl{AIOj}}}LCl}\",[]]A`[\"{AIClLIf}\",[]]0000000000000000000000000000000000000000000000000000000000000Ce[\"{{{AICl{CBnB@Kj}}{Nd{BEGj}}}{{AICl{CBnB@Kj}}}}\",[]]Ce[\"{{{AICl{CBnB@Kn}}{Nd{BEGl}}}{{AICl{CBnB@Kn}}}}\",[]]A`[\"{BGGjJJ`}\",[]]o[\"{B@KjNd}\",[]]o[\"{B@KnNd}\",[]]Aj[\"{{{AICl{ABMj}}}HK`}\",[]]Ck[\"{{{AICl{CBnAJCh}}{AICl{{LAf{Ml}}}}}{{LGn{HK`AJEd}}}}\",[]]n[\"{{}LCl}\",[]]Ak[\"{{{AICl{AIOj}}}AHFn}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Bi[\"{{{AICl{CBn}}}{{AICl{CBnc}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bc[\"{{AICl{AICl{CBnc}}}HK`{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AICl{CBnAIOl}}OIjLGjLIl}{{LGn{HK`BBDn}}}}\",[]]A`[\"{AHHbCNf}\",[]]B`[\"{{AICl{AICl{c}}}CNf{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BCDhBEOn}\",[]]A`[\"{BDLdLIj}\",[]]Aa[\"{BCDlBF@`}\",[]]A`[\"{BDLhLIl}\",[]]o[\"{OLdLIl}\",[]]Aa[\"{BCDhAJAb}\",[]]Aa[\"{BCDlAJAd}\",[]]o[\"{LIjCIl}\",[]]o[\"{LIlCIl}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]Aj[\"{{{AICl{BAGn}}}CNf}\",[]]Bd[\"{{{AICl{BBDh}}}{{LH`{AJCn}}}}\",[]]Bd[\"{{{AICl{ABMj}}}{{LH`{AJCn}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Cb[\"{{{AICl{BCF`}}{AICl{AJCf}}CIl}{{Nd{AOHl}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Bg[\"{{{AICl{AIOj}}LGh}{{LGd{AHKd}}}}\",[]]Bj[\"{{{AICl{CBnAIOl}}LGj}{{LGd{AHKf}}}}\",[]]Aa[\"{AHFjAHKf}\",[]]Db[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}}{{CLl{{LH`{HK`}}}}}}\",[]]00Ec[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}{AICl{{LAf{Ml}}}}}{{CLl{{LH`{LCl}}}}}}\",[]]Eg[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}{AICl{{LAf{Ml}}}}}{{CLl{{LGn{LClAJEd}}}}}}\",[]]1Bm[\"{{{AICl{JKf}}{AICl{CLb}}}{{LH`{HK`}}}}\",[]]Cd[\"{{{AICl{CBnAJBl}}LCl}{{LGn{{LGd{JFj}}AHKj}}}}\",[]]Cd[\"{{{AICl{CBnBFEd}}LCl}{{LGn{{LGd{JFj}}AHKj}}}}\",[]]Cn[\"{{{AICl{CBnAJBl}}{AICl{CBn{LAf{Ml}}}}}{{LGn{HK`BBHn}}}}\",[]]Bj[\"{{{AICl{CLd}}AJBb}{{LGn{HK`BEOf}}}}\",[]]Bn[\"{{{AICl{CBnAJBf}}OIj}{{AICl{CBnAJBf}}}}\",[]]Bn[\"{{{AICl{CBnAJBh}}OIj}{{AICl{CBnAJBh}}}}\",[]]A`[\"{BBIdADn}\",[]]o[\"{JKdCIl}\",[]]o[\"{{}AJCn}\",[]]0o[\"{{}AICl}\",[]]Bj[\"{{ABNb{Nd{B@Kj}}}{{LGn{AIOfBCDn}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bh[\"{{{AICl{AHHb}}AJCn}{{LHb{AHLb}}}}\",[]]A`[\"{AHKdCIh}\",[]]Aj[\"{{{AICl{AIOl}}}CIh}\",[]]A`[\"{AHKfCIh}\",[]]Ce[\"{{{AICl{CBnAJBf}}{LGd{LIf}}}{{AICl{CBnAJBf}}}}\",[]]Ce[\"{{{AICl{CBnAJBh}}{LGd{LIf}}}{{AICl{CBnAJBh}}}}\",[]]Aa[\"{ACAjAJCn}\",[]]Aa[\"{ACAlAJCn}\",[]]Aa[\"{ACAnAJCn}\",[]]o[\"{A@bCIh}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIh}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIh}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbCIh}\",[]]Bo[\"{{{AICl{CBnBCGh}}ABMf}{{AICl{CBnBCGh}}}}\",[]]Bo[\"{{{AICl{CBnBCGj}}ABMf}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]k[\"{{}}\",[]]0000000000Bh[\"{ABNbc{}}\",[\"IntoFuture::IntoFuture\"]]11A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]o[\"{A@bLCl}\",[]]Ao[\"{{AIClc}{{OJ`{c}}}{}}\",[\"S\"]]0C`[\"{{{AICl{CBnAIOn}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]Bn[\"{{{AICl{CBnNf}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]C`[\"{{{AICl{CBnAJ@`}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]Bn[\"{{{AICl{CBnNh}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]Bk[\"{{{AICl{CBnJKf}}OIj{AICl{BCF`}}}HK`}\",[]]A`[\"{BEGfLGd}\",[]]A`[\"{BEGhLGd}\",[]]Bn[\"{{{AICl{LIj}}{AICl{LIj}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{LGh}}{AICl{LGh}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AOFf}}{AICl{AOFf}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{B@Gh}}{AICl{B@Gh}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{CMb}}{AICl{CMb}}}{{LGd{ABOj}}}}\",[]]Bl[\"{{{AICl{Ob}}{AICl{Ob}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AC@h}}{AICl{AC@h}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{LIl}}{AICl{LIl}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BEHl}}{AICl{BEHl}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{LGj}}{AICl{LGj}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AHKl}}{AICl{AHKl}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BAKf}}{AICl{BAKf}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AJEf}}{AICl{AJEf}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AOFh}}{AICl{AOFh}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BDM`}}{AICl{BDM`}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BEHh}}{AICl{BEHh}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{B@Gj}}{AICl{B@Gj}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{CMd}}{AICl{CMd}}}{{LGd{ABOj}}}}\",[]]Bl[\"{{{AICl{Od}}{AICl{Od}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AC@j}}{AICl{AC@j}}}{{LGd{ABOj}}}}\",[]]Be[\"{{{AICl{AIOj}}}{{AC@`{AHK`}}}}\",[]]Bk[\"{{{AICl{AIOl}}LGj}{{LGn{AJBdAIOb}}}}\",[]]Df[\"{{{AICl{CBnAJBl}}{AICl{CBn{LAf{JFh}}}}}{{LGn{{LGd{LCl}}AHKj}}}}\",[]]Cc[\"{{{AICl{CBnAJBl}}LCl}{{LGn{{AA`{Ml}}BBI`}}}}\",[]]Bb[\"{{{AICl{CBnAIOl}}AC@j}AJBn}\",[]]Aj[\"{{{AICl{AHIh}}}LCl}\",[]]Aj[\"{{{AICl{AHIj}}}LCl}\",[]]Bb[\"{{{AICl{CBnAIOl}}AC@j}AJCj}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]A`[\"{BAHdLGd}\",[]]A`[\"{BAHfLGd}\",[]]Ce[\"{{{AICl{CBnB@Kj}}{Nd{AJDf}}}{{AICl{CBnB@Kj}}}}\",[]]Ce[\"{{{AICl{CBnB@Kn}}{Nd{AJDh}}}{{AICl{CBnB@Kn}}}}\",[]]Ce[\"{{{AICl{CBnB@Ej}}{Nd{AJDn}}}{{AICl{CBnB@Ej}}}}\",[]]Ce[\"{{{AICl{CBnB@En}}{Nd{AJE`}}}{{AICl{CBnB@En}}}}\",[]]o[\"{A@bCIl}\",[]]Bn[\"{{{AICl{CBnBEMh}}CIh}{{AICl{CBnBEMh}}}}\",[]]Bn[\"{{{AICl{CBnBEMj}}CIh}{{AICl{CBnBEMj}}}}\",[]]Ak[\"{{{AICl{AIOj}}}BFLn}\",[]]Aj[\"{{{AICl{CLd}}}BBIn}\",[]]Ai[\"{{{Nd{B@Kl}}}B@Kj}\",[]]Ai[\"{{{Nd{B@L`}}}B@Kn}\",[]]Aj[\"{{BCFjc}BCFjACA`}\",[\"\"]]Aj[\"{{BCFlc}BCFlACA`}\",[\"\"]]Bm[\"{{{AICl{CBnAJCh}}JFh}{{LGn{HK`AJEd}}}}\",[]]n[\"{{}LFh}\",[]]o[\"{OHfOHf}\",[]]0000Bd[\"{{{AICl{BGBh}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{BGBj}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{BHBj}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{BHCd}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{AIOj}}}{{LGd{BCDn}}}}\",[]]Ck[\"{{{AICl{ABMj}}B@EjAJCn{AICl{CIf}}}{{LGn{AIOfB@Gb}}}}\",[]]Ak[\"{{{AICl{B@Kb}}}ABMf}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]An[\"{{AJBjCNf}{{LHb{LCl}}}}\",[]]Cj[\"{{{AICl{OL`}}}{{LGd{{LCj{{Nl{AHIh}}{Nl{AHJf}}}}}}}}\",[]]Ck[\"{{{AICl{AJDj}}}{{LGd{{LCj{{Nl{AHIh}}{Nl{AHJf}}}}}}}}\",[]]Cj[\"{{{AICl{OLb}}}{{LGd{{LCj{{Nl{AHIj}}{Nl{AHJh}}}}}}}}\",[]]Ck[\"{{{AICl{AJDl}}}{{LGd{{LCj{{Nl{AHIj}}{Nl{AHJh}}}}}}}}\",[]]A`[\"{BFKbAA`}\",[]]A`[\"{BHFhAA`}\",[]]A`[\"{BFKdAA`}\",[]]A`[\"{BHFjAA`}\",[]]An[\"{{{AICl{CBnBGBh}}}B@Gh}\",[]]An[\"{{{AICl{CBnBGBj}}}B@Gj}\",[]]An[\"{{{AICl{CBnBHBj}}}B@Gj}\",[]]An[\"{{{AICl{CBnBHCd}}}B@Gj}\",[]]Ak[\"{{{AICl{BBIl}}}B@Ih}\",[]]Ba[\"{{{AICl{CBnAIOl}}BCEd}HK`}\",[]]Bo[\"{{{AICl{CBnABMl}}BDM`BAGn}{{LGd{BCEd}}}}\",[]]B`[\"{{{AICl{OL`}}B@GhCMb}CKh}\",[]]Bn[\"{{{AICl{B@Kl}}CIjB@Gh}{{LGn{CKhBF@f}}}}\",[]]Ba[\"{{{AICl{AJDj}}B@GhCMb}CKh}\",[]]Bn[\"{{{AICl{BDOn}}CIjB@Gh}{{LGn{CKhBF@f}}}}\",[]]B`[\"{{{AICl{OLb}}B@GjCMd}CKj}\",[]]Bn[\"{{{AICl{B@L`}}CIjB@Gj}{{LGn{CKjBF@l}}}}\",[]]Ba[\"{{{AICl{AJDl}}B@GjCMd}CKj}\",[]]Bn[\"{{{AICl{BE@`}}CIjB@Gj}{{LGn{CKjBF@l}}}}\",[]]Bn[\"{{{AICl{BGMf}}CIjB@Gf}{{LGn{CKjBF@l}}}}\",[]]Aj[\"{{}{{OKj{c}}}{}}\",[\"T\"]]0A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Bn[\"{{{AICl{CBnB@Kj}}LCl}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}LCl}{{AICl{CBnB@Kn}}}}\",[]]Aj[\"{{{AICl{BBDh}}}CNf}\",[]]0Aj[\"{{{AICl{BBIl}}}CNf}\",[]]Bk[\"{{{AICl{AIOl}}LGj}{{LGn{AHI`AIOb}}}}\",[]]Cb[\"{{{AICl{BCF`}}{AICl{AJBj}}CIl}{{Nd{AOHl}}}}\",[]]Ak[\"{{{AICl{ABNb}}}B@Gh}\",[]]Ak[\"{{{AICl{ABNd}}}B@Gj}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bf[\"{{{AICl{CBnAIOl}}}{{LGd{OIj}}}}\",[]]Ce[\"{{{AICl{CBnBCGh}}{Nd{AOGd}}}{{AICl{CBnBCGh}}}}\",[]]Ce[\"{{{AICl{CBnBCGj}}{Nd{AOGf}}}{{AICl{CBnBCGj}}}}\",[]]43A`[\"{ACAjLGd}\",[]]A`[\"{ACAlLGd}\",[]]A`[\"{ACAnLGd}\",[]]Ai[\"{{{AICl{OM`}}}LCl}\",[]]Bo[\"{{{AICl{CBnBHBj}}ABMf}{{AICl{CBnBHBj}}}}\",[]]Bo[\"{{{AICl{CBnBHCd}}ABMf}{{AICl{CBnBHCd}}}}\",[]]Bj[\"{{{AICl{AJCh}}CAh}{{LGn{HK`B@Ff}}}}\",[]]Bm[\"{{{AICl{CBnAJCj}}CAh}{{LGn{HK`B@Fn}}}}\",[]]<;A`[\"{JKdAOHj}\",[]]A`[\"{AODnJKd}\",[]]Bi[\"{{{Nd{B@Ef}}JKh}{{LGn{BDOjBFL`}}}}\",[]]Bi[\"{{{Nd{B@Kf}}JKh}{{LGn{BDOnBFL`}}}}\",[]]Bi[\"{{{Nd{B@Ef}}JKh}{{LGn{BDOlBFLb}}}}\",[]]Bi[\"{{{Nd{B@Kf}}JKh}{{LGn{BE@`BFLb}}}}\",[]]Co[\"{{{AICl{CBnAJCh}}{AICl{CBn{LAf{JFh}}}}}{{LGn{OMlAJEd}}}}\",[]]Co[\"{{{AICl{CBnAJCj}}{AICl{CBn{LAf{JFh}}}}}{{LGn{OMnAJEf}}}}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]A`[\"{AHHbCNf}\",[]]E`[\"{{{AICl{CBnBBF`}}c}{{AICl{CBnBBF`}}}{{A`{}{{LGf{{Nl{BGBh}}}}}}CM`CMf}}\",[\"F\"]]E`[\"{{{AICl{CBnBBFb}}c}{{AICl{CBnBBFb}}}{{A`{}{{LGf{{Nl{BGBj}}}}}}CM`CMf}}\",[\"F\"]]Bj[\"{{{AICl{BBDh}}}{{A@j{{Nl{AHL`}}}}}}\",[]]o[\"{A@bCIl}\",[]]Aa[\"{AOHjAHIl}\",[]]76Aa[\"{AJCbABNb}\",[]]Aa[\"{AJCdABNd}\",[]]Cf[\"{{{AICl{CBnBEKj}}{LGd{ABMf}}}{{AICl{CBnBEKj}}}}\",[]]Cf[\"{{{AICl{CBnBEKl}}{LGd{ABMf}}}{{AICl{CBnBEKl}}}}\",[]]9;:An[\"{{{AICl{AOHl}}ABMf}HK`}\",[]]B`[\"{{{AICl{CBnAIOn}}CIh}HK`}\",[]]An[\"{{{AICl{CBnNf}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnJGb}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnOJf}}CIh}HK`}\",[]]B`[\"{{{AICl{CBnAJ@`}}CIh}HK`}\",[]]An[\"{{{AICl{CBnNh}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnJGd}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnOJh}}CIh}HK`}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Bh[\"{{{AICl{AIOj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OL`}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OLb}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDl}}}{{LGd{{Nl{Nb}}}}}}\",[]]Dm[\"{{{AICl{CBnAJBl}}{AICl{CBnOHh}}{AICl{CBnOKh}}}{{CLl{{LGn{HK`AHKj}}}}}}\",[]]Db[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}}{{CLl{{LH`{HK`}}}}}}\",[]]Db[\"{{{AICl{CBnAIOl}}OIjB@Ih{AICl{CBn{AA`{Ml}}}}}{{LGd{ACAl}}}}\",[]]Cf[\"{{{AICl{CBnBCGh}}{AICl{CIf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{AICl{CIf}}}{{AICl{CBnBCGj}}}}\",[]]Ak[\"{{{AICl{AIOj}}}B@Jj}\",[]]Bj[\"{{{AICl{AIOj}}JFh}{{LGn{HK`BEHj}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Ai[\"{{{AICl{OId}}}LCl}\",[]]Ai[\"{{{AICl{OIf}}}LCl}\",[]]De[\"{{{Nd{B@El}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OL`}}B@Gb}}}}\",[]]Bo[\"{{{Nd{B@Kl}}CIj{AICl{BFEb}}}{{Nl{OL`}}}}\",[]]De[\"{{{Nd{BDOj}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OL`}}B@Gb}}}}\",[]]Bo[\"{{{Nd{BDOn}}CIj{AICl{BFEb}}}{{Nl{OL`}}}}\",[]]De[\"{{{Nd{B@F`}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OLb}}B@Gd}}}}\",[]]Bo[\"{{{Nd{B@L`}}CIj{AICl{BFEb}}}{{Nl{OLb}}}}\",[]]De[\"{{{Nd{BDOl}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OLb}}B@Gd}}}}\",[]]Bo[\"{{{Nd{BE@`}}CIj{AICl{BFEb}}}{{Nl{OLb}}}}\",[]]De[\"{{{Nd{BGMd}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OLb}}B@G`}}}}\",[]]Bo[\"{{{Nd{BGMf}}CIj{AICl{BFEb}}}{{Nl{OLb}}}}\",[]]Cc[\"{{{AICl{BEGj}}{AICl{{LAf{Ml}}}}}{{Nl{OGl}}}}\",[]]Cc[\"{{{AICl{BEGl}}{AICl{{LAf{Ml}}}}}{{Nl{OGn}}}}\",[]]Bn[\"{{AJCnBBDjA@b}{{LHb{{LCj{ABMhAIOh}}}}}}\",[]]Bd[\"{{{AICl{AIOl}}}{{AICl{OLb}}}}\",[]]A`[\"{AODnJKd}\",[]]Cb[\"{{{AICl{AOGd}}CMbAJCnB@GhOIj}{{LGd{AJBf}}}}\",[]]Cb[\"{{{AICl{BCFj}}CMbAJCnB@GhOIj}{{LGd{AJBf}}}}\",[]]Cb[\"{{{AICl{AOGf}}CMdAJCnB@GjOIj}{{LGd{AJBh}}}}\",[]]Cb[\"{{{AICl{BCFl}}CMdAJCnB@GjOIj}{{LGd{AJBh}}}}\",[]]B`[\"{{{AICl{CBnAIOl}}OIj}HK`}\",[]]Bo[\"{{{AICl{CBnAIOf}}}{{LGn{{Nl{Nb}}BCDn}}}}\",[]]Bh[\"{{{AICl{AIOj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OL`}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OLb}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDl}}}{{LGd{{Nl{Nb}}}}}}\",[]]o[\"{AJ@fMl}\",[]]o[\"{AJ@hMl}\",[]]Ai[\"{{{AICl{CIf}}}CNf}\",[]]Aj[\"{{{AICl{AIOn}}}CIl}\",[]]Ah[\"{{{AICl{Nf}}}CIl}\",[]]Bn[\"{{{AICl{CBnAHGl}}CIl}{{AICl{CBnAHGl}}}}\",[]]Ai[\"{{{AICl{JGb}}}CIl}\",[]]Bn[\"{{{AICl{CBnAOEl}}CIl}{{AICl{CBnAOEl}}}}\",[]]Ai[\"{{{AICl{OJf}}}CIl}\",[]]Bn[\"{{{AICl{CBnBAJf}}CIl}{{AICl{CBnBAJf}}}}\",[]]Aj[\"{{{AICl{AJ@`}}}CIl}\",[]]Ah[\"{{{AICl{Nh}}}CIl}\",[]]Bn[\"{{{AICl{CBnAHGn}}CIl}{{AICl{CBnAHGn}}}}\",[]]Ai[\"{{{AICl{JGd}}}CIl}\",[]]Bn[\"{{{AICl{CBnAOEn}}CIl}{{AICl{CBnAOEn}}}}\",[]]Ai[\"{{{AICl{OJh}}}CIl}\",[]]Bn[\"{{{AICl{CBnBAJh}}CIl}{{AICl{CBnBAJh}}}}\",[]]Aa[\"{AJBlBFEd}\",[]]Ai[\"{{{AICl{OL`}}}CNf}\",[]]Aj[\"{{{AICl{AJDj}}}CNf}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]Ai[\"{{{AICl{OLb}}}CNf}\",[]]Aj[\"{{{AICl{AJDl}}}CNf}\",[]]Co[\"{{{AICl{OL`}}B@Gh{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]D`[\"{{{AICl{AJDj}}B@Gh{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]Co[\"{{{AICl{OLb}}B@Gj{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]D`[\"{{{AICl{AJDl}}B@Gj{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]Bn[\"{{{AICl{CBnBEMh}}CIh}{{AICl{CBnBEMh}}}}\",[]]Bn[\"{{{AICl{CBnBEMj}}CIh}{{AICl{CBnBEMj}}}}\",[]]Cc[\"{{{AICl{CBnOL`}}}{{LGd{{OIl{{Nl{AHJf}}}}}}}}\",[]]Cd[\"{{{AICl{CBnAJDj}}}{{LGd{{OIl{{Nl{AHJf}}}}}}}}\",[]]Cc[\"{{{AICl{CBnOLb}}}{{LGd{{OIn{{Nl{AHJh}}}}}}}}\",[]]Cd[\"{{{AICl{CBnAJDl}}}{{LGd{{OIn{{Nl{AHJh}}}}}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]De[\"{{{AICl{CBnBCGh}}c{AICl{CIf}}}{{AICl{CBnBCGh}}}{{JFf{CLb}}}}\",[\"\"]]De[\"{{{AICl{CBnBCGj}}c{AICl{CIf}}}{{AICl{CBnBCGj}}}{{JFf{CLb}}}}\",[\"\"]]Ci[\"{{{AICl{CBnOL`}}{AICl{{LAf{Ml}}}}}{{LGn{CNfJHh}}}}\",[]]Cj[\"{{{AICl{CBnAJDj}}{AICl{{LAf{Ml}}}}}{{LGn{CNfJHh}}}}\",[]]Cj[\"{{{AICl{CBnOLb}}{AICl{{LAf{Ml}}}}}{{LGn{CNfBBIj}}}}\",[]]Ck[\"{{{AICl{CBnAJDl}}{AICl{{LAf{Ml}}}}}{{LGn{CNfBBIj}}}}\",[]]o[\"{A@bCNf}\",[]]Bn[\"{{{AICl{CBnBCGh}}LIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LIl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]Ca[\"{{{AICl{CBnAJBl}}}{{LGn{{LGd{LIj}}AJC`}}}}\",[]]Ca[\"{{{AICl{CBnBFEd}}}{{LGn{{LGd{LIj}}AJC`}}}}\",[]]Ca[\"{{{AICl{CBnAJBn}}}{{LGn{{LGd{LIl}}B@Fn}}}}\",[]]Ak[\"{{{AICl{AIOf}}}AJCn}\",[]]Ak[\"{{{AICl{AIOj}}}AJCn}\",[]]Ak[\"{{{AICl{ABNb}}}AJCn}\",[]]Bg[\"{{{AICl{CLd}}}{{LGn{AJCnAINl}}}}\",[]]Ak[\"{{{AICl{ABNd}}}AJCn}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]B`[\"{{LIjc}LIj{{CKf{LIj}}}}\",[\"\"]]B`[\"{{LGhc}LGh{{CKf{LGh}}}}\",[\"\"]]B`[\"{{LIlc}LIl{{CKf{LIl}}}}\",[\"\"]]B`[\"{{LGjc}LGj{{CKf{LGj}}}}\",[\"\"]]20Ae[\"{{ABMfCIl}ADn}\",[]]Bn[\"{{{AICl{CBnBCGh}}ADl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}ADl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{JKdABMf}\",[]]B`[\"{{}{{AICl{{LAf{BFDf}}}}}}\",[]]Ao[\"{{AICl{A@Mf{CBnMl}}}HK`}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{LGd{{Nd{OKn}}}}}}\",[]]Bn[\"{{{AICl{CBnBBF`}}CNf}{{AICl{CBnBBF`}}}}\",[]]Bn[\"{{{AICl{CBnBBFb}}CNf}{{AICl{CBnBBFb}}}}\",[]]Aj[\"{{{AICl{AHJf}}}CIl}\",[]]Aj[\"{{{AICl{AHJh}}}CIl}\",[]]A`[\"{AC@dLGd}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Bf[\"{{{AICl{CIf}}}{{LHb{CIlBAJj}}}}\",[]]Bm[\"{{{AICl{ABMj}}{Nl{BBDh}}}{{LH`{HK`}}}}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]Cj[\"{{{AICl{OKl}}BBDjBAKd{AICl{A@b}}}{{LCj{AJCnABMh}}}}\",[]]Cb[\"{{{AICl{CBnAIOl}}LGjAJBd}{{LGn{AJBdBEOh}}}}\",[]]Am[\"{{{AICl{AIOj}}CIl}HK`}\",[]]B`[\"{{{AICl{CBnAIOl}}CIl}HK`}\",[]]A`[\"{BBIbCNf}\",[]]B`[\"{{{AICl{CBnJKd}}BBId}HK`}\",[]]Aa[\"{AOHjAHIl}\",[]]Bm[\"{{{AICl{OKn}}AHLb}{{LH`{{Nl{BBDh}}}}}}\",[]]Bn[\"{{{AICl{B@Lh}}AHLb}{{LH`{{Nl{BBDh}}}}}}\",[]]Bn[\"{{{AICl{AOH`}}AHLb}{{LH`{{Nl{BBDh}}}}}}\",[]]Ci[\"{{{AICl{CBnOL`}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKh}}}}\",[]]Cj[\"{{{AICl{CBnAJDj}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKh}}}}\",[]]Ci[\"{{{AICl{CBnOLb}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKj}}}}\",[]]Cj[\"{{{AICl{CBnAJDl}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKj}}}}\",[]]Cb[\"{{{AICl{ACAb}}HJnAJDdABMf}{{LGn{HK`BCGb}}}}\",[]]Cb[\"{{{AICl{BAFn}}HJnAJDdABMf}{{LGn{HK`BCGb}}}}\",[]]Cb[\"{{{AICl{B@Ij}}HJnAJDdABMf}{{LGn{HK`BCGb}}}}\",[]]Cb[\"{{{AICl{ACAd}}HJnAJDdABMf}{{LGn{HK`BCGd}}}}\",[]]Cb[\"{{{AICl{BAG`}}HJnAJDdABMf}{{LGn{HK`BCGd}}}}\",[]]Cb[\"{{{AICl{B@Il}}HJnAJDdABMf}{{LGn{HK`BCGd}}}}\",[]]Al[\"{{}{{LGn{B@EhBFLb}}}}\",[]]Bm[\"{{{AICl{AIOj}}LGh}{{LGd{{Nl{AIOn}}}}}}\",[]]Bo[\"{{{AICl{AIOl}}LGj}{{LGd{{AICl{AJ@`}}}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Aj[\"{{{AICl{AIOj}}}HK`}\",[]]Am[\"{{{AICl{CBnAIOl}}}HK`}\",[]]Bf[\"{{{AICl{AINb}}}{{LGn{A@bJHb}}}}\",[]]Bg[\"{{{AICl{AINb}}}{{LGn{AHHbJHb}}}}\",[]]Bf[\"{{{AICl{AINb}}}{{LGn{A@dJHb}}}}\",[]]Bf[\"{{{AICl{AINb}}}{{LGn{A@fJHb}}}}\",[]]Be[\"{{{AICl{AINb}}}{{LGn{O`JHb}}}}\",[]]Bg[\"{{{AICl{AINb}}}{{LGn{ABLhJHb}}}}\",[]]Ak[\"{{{AICl{BBIl}}}B@Ih}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{AOFf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{AOFh}}}{{AICl{CBnBCGj}}}}\",[]]<;Aj[\"{{{AICl{ABMj}}}LCl}\",[]]Aj[\"{{{AICl{ABMl}}}LCl}\",[]]Bc[\"{{{AICl{AIOj}}AJCnAJBb}ABOh}\",[]]Co[\"{{{AICl{CBnAIOl}}AHI`AJBdOIj}{{LGn{{LCj{LGjCNf}}AHJl}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIj}{{AICl{CBnBCGj}}}}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]Bg[\"{{{AICl{BBIl}}AJEb}{{LH`{LCl}}}}\",[]]A`[\"{AHHbCIl}\",[]]10Aj[\"{{{AICl{A@b}}}BCGf}\",[]]Ce[\"{{{AICl{CBnB@Kj}}{Nd{BCGh}}}{{AICl{CBnB@Kj}}}}\",[]]Ce[\"{{{AICl{CBnB@Ej}}{Nd{BCGh}}}{{AICl{CBnB@Ej}}}}\",[]]Ce[\"{{{AICl{CBnB@Kn}}{Nd{BCGj}}}{{AICl{CBnB@Kn}}}}\",[]]Ce[\"{{{AICl{CBnB@En}}{Nd{BCGj}}}{{AICl{CBnB@En}}}}\",[]]Aa[\"{B@KjBGCj}\",[]]Aa[\"{B@KnBGCl}\",[]]Bj[\"{{{AA`{BBDj}}BAKd}{{LGn{B@KjJHl}}}}\",[]]Bj[\"{{{AA`{BBDj}}BAKd}{{LGn{B@KnJHn}}}}\",[]]Co[\"{{{AICl{CBnAJCh}}{AICl{CBn{LAf{JFh}}}}}{{LGn{HK`AJEd}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]A`[\"{BEGfCIl}\",[]]A`[\"{BEGhCIl}\",[]]Bc[\"{{{AICl{AIOj}}}{{LGd{LCl}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Aj[\"{{{AICl{AHHh}}}LCl}\",[]]Be[\"{{{AICl{ABMj}}{LGd{B@Kj}}}HK`}\",[]]Bn[\"{{{AICl{CBnABMl}}{LGd{{Nd{B@Kn}}}}}HK`}\",[]]Bd[\"{{{AICl{BFDf}}}{{LGd{BAKb}}}}\",[]]o[\"{{}AICl}\",[]]n[\"{{}OHf}\",[]]0000Bn[\"{{{AICl{CBnBCGh}}LCl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LCl}{{AICl{CBnBCGj}}}}\",[]]o[\"{CIlLIj}\",[]]o[\"{CIlLIl}\",[]]A`[\"{BAH`CIl}\",[]]Bf[\"{{{AICl{BFLn}}{AICl{BFLn}}}CNf}\",[]]Bo[\"{{{AICl{CBnBBF`}}ABMf}{{AICl{CBnBBF`}}}}\",[]]Bo[\"{{{AICl{CBnBBFb}}ABMf}{{AICl{CBnBBFb}}}}\",[]]Ae[\"{{LClCIl}BAFn}\",[]]Ae[\"{{LClCIl}BAG`}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]7Bg[\"{{{AICl{AIOl}}LGj}{{LGd{AJBd}}}}\",[]]An[\"{{{AICl{AIOj}}JFh}B@Kd}\",[]]Am[\"{{{AICl{AIOj}}LIj}HK`}\",[]]B`[\"{{{AICl{CBnAIOl}}LIl}HK`}\",[]]Ce[\"{{{AICl{CBnBBF`}}{AA`{CIj}}}{{AICl{CBnBBF`}}}}\",[]]Ce[\"{{{AICl{CBnBBFb}}{AA`{CIj}}}{{AICl{CBnBBFb}}}}\",[]]Fg[\"{{{AICl{BGGl}}{AICl{BBDj}}{AICl{{LAf{BBDj}}}}{AICl{AJCl}}{AICl{{LAf{Ml}}}}ACBd}{{LGn{BEObJH`}}}}\",[]]Af[\"{{BCFjOIj}BCFj}\",[]]Af[\"{{BCFlOIj}BCFl}\",[]]A`[\"{BAH`CIl}\",[]]Al[\"{B@Lj{{LGn{CMbJHj}}}}\",[]]Ak[\"{B@Lj{{LGn{ObJHj}}}}\",[]]Ai[\"{B@Lj{{LHd{CMd}}}}\",[]]Ah[\"{B@Lj{{LHd{Od}}}}\",[]]o[\"{OHfOHf}\",[]]Bo[\"{{{AICl{CBnBEMh}}ABMf}{{AICl{CBnBEMh}}}}\",[]]Bo[\"{{{AICl{CBnBEMj}}ABMf}{{AICl{CBnBEMj}}}}\",[]]Bb[\"{{{AICl{OL`}}}{{LGd{CNf}}}}\",[]]Bc[\"{{{AICl{AJDj}}}{{LGd{CNf}}}}\",[]]Bb[\"{{{AICl{OLb}}}{{LGd{CNf}}}}\",[]]Bc[\"{{{AICl{AJDl}}}{{LGd{CNf}}}}\",[]]Bg[\"{{{AICl{AIOj}}}{{LGn{HK`BCDn}}}}\",[]]Ba[\"{{{AICl{BEHb}}LGhAHI`}CNf}\",[]]Ba[\"{{{AICl{BEHd}}LGjAHI`}CNf}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{ABMf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{ABMf}}}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Bl[\"{{{AICl{CBnAIOn}}OIjOIjCNfCNfCIl}HK`}\",[]]Bj[\"{{{AICl{CBnNf}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnJGb}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnOJf}}OIjOIjCNfCNfCIl}HK`}\",[]]Bl[\"{{{AICl{CBnAJ@`}}OIjOIjCNfCNfCIl}HK`}\",[]]Bj[\"{{{AICl{CBnNh}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnJGd}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnOJh}}OIjOIjCNfCNfCIl}HK`}\",[]]A`[\"{BAH`CIl}\",[]]Ak[\"{{{AICl{OM`}}Od}CIl}\",[]]Bi[\"{{{AICl{CBnAJCf}}CIl}{{LHb{HK`}}}}\",[]]<;A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Cf[\"{{{AICl{CBnBCGh}}{LGd{BEKj}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{BEKl}}}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Bi[\"{{{AICl{CBnAINb}}}{{LGn{A@bJHb}}}}\",[]]Bj[\"{{{AICl{CBnAINb}}}{{LGn{AHHbJHb}}}}\",[]]Bi[\"{{{AICl{CBnAINb}}}{{LGn{A@dJHb}}}}\",[]]Bi[\"{{{AICl{CBnAINb}}}{{LGn{A@fJHb}}}}\",[]]Bh[\"{{{AICl{CBnAINb}}}{{LGn{O`JHb}}}}\",[]]Bj[\"{{{AICl{CBnAINb}}}{{LGn{ABLhJHb}}}}\",[]]Cc[\"{{{Nd{AIOh}}CIlCNf}{{LHb{{LCj{BBIdBBId}}}}}}\",[]]Bn[\"{{{AICl{CBnB@Kj}}CIl}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}CIl}{{AICl{CBnB@Kn}}}}\",[]]Aj[\"{{{AICl{AIOj}}}CNf}\",[]]Ak[\"{{{AICl{BBDh}}}B@Ih}\",[]]0Ci[\"{{{AICl{CBnBBF`}}CIh}{{LGn{{AICl{CBnBBF`}}AOEb}}}}\",[]]Ci[\"{{{AICl{CBnBBFb}}CIh}{{LGn{{AICl{CBnBBFb}}AOEd}}}}\",[]]A`[\"{AHHbCIh}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{BEMh}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{BEMj}}}{{AICl{CBnBCGj}}}}\",[]]Bg[\"{{{AICl{CBnAIOl}}}{{LGd{BAGn}}}}\",[]]Cf[\"{{{AICl{CBnB@Kj}}{LGd{B@Lb}}}{{AICl{CBnB@Kj}}}}\",[]]Cf[\"{{{AICl{CBnB@Kn}}{LGd{B@Lb}}}{{AICl{CBnB@Kn}}}}\",[]]Cf[\"{{{AICl{CBnB@Kj}}{LGd{B@Ld}}}{{AICl{CBnB@Kj}}}}\",[]]Cf[\"{{{AICl{CBnB@Kn}}{LGd{B@Ld}}}{{AICl{CBnB@Kn}}}}\",[]]Cc[\"{{}{{LGn{{LCj{{AA`{BBDj}}BAKd}}{Nl{JGn}}}}}}\",[]]Bn[\"{{{AICl{CBnBEKj}}LIj}{{AICl{CBnBEKj}}}}\",[]]Bn[\"{{{AICl{CBnBEKl}}LIl}{{AICl{CBnBEKl}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bo[\"{{{AICl{CBnB@Kj}}ABMf}{{AICl{CBnB@Kj}}}}\",[]]Bo[\"{{{AICl{CBnB@Kn}}ABMf}{{AICl{CBnB@Kn}}}}\",[]]Ci[\"{{{AICl{CLd}}{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AINl}}}}\",[]]Bj[\"{{{AICl{BBIl}}AJEbLCl}{{LH`{HK`}}}}\",[]]054Bm[\"{{{AICl{OL`}}}{{LGn{{LGd{BFEb}}JHh}}}}\",[]]Bn[\"{{{AICl{AJDj}}}{{LGn{{LGd{BFEb}}JHh}}}}\",[]]Bn[\"{{{AICl{OLb}}}{{LGn{{LGd{BFEb}}BBIj}}}}\",[]]Bo[\"{{{AICl{AJDl}}}{{LGn{{LGd{BFEb}}BBIj}}}}\",[]]Aj[\"{{{AICl{AHJf}}}CIl}\",[]]Aj[\"{{{AICl{AHJh}}}CIl}\",[]]Bk[\"{{{AICl{ABMj}}{LGd{{Nd{BEHb}}}}}HK`}\",[]]Cc[\"{{{AICl{CBnAIOl}}{LGd{{AICl{BEHd}}}}OIj}HK`}\",[]]Aj[\"{{{AICl{ABMl}}}CIl}\",[]]Bf[\"{{{AICl{AHI`}}{AICl{AHI`}}}CNf}\",[]]Bn[\"{{{AICl{CBnBAJf}}ADl}{{AICl{CBnBAJf}}}}\",[]]Bn[\"{{{AICl{CBnBAJh}}ADl}{{AICl{CBnBAJh}}}}\",[]]Ak[\"{{{AICl{AHL`}}}B@Ih}\",[]]0Bd[\"{{{AICl{AIOj}}}{{AC@`{JIh}}}}\",[]]Cb[\"{{{AICl{AIOl}}LGj}{{LGn{{LGd{AJCn}}AIOb}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bn[\"{{{AICl{CBnBCGh}}LIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LIl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]Bo[\"{{{AICl{AHHb}}{AICl{CIf}}}{{LH`{BCGf}}}}\",[]]Em[\"{{{AICl{AIOj}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGO`}}}}\",[]]El[\"{{{AICl{OL`}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGO`}}}}\",[]]Em[\"{{{AICl{AJDj}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGO`}}}}\",[]]El[\"{{{AICl{OLb}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGOb}}}}\",[]]Em[\"{{{AICl{AJDl}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGOb}}}}\",[]]Am[\"{{{AICl{CBnAHHf}}}HK`}\",[]]0Al[\"{{{AICl{AIOl}}Od}CIl}\",[]]Bl[\"{{{AICl{AIOj}}}{{AC@b{{LGd{AJCn}}}}}}\",[]]Bg[\"{{{AICl{CLd}}}{{LGn{BDL`AINl}}}}\",[]]>=E`[\"{{{AICl{BGGl}}{AICl{{LAf{Ml}}}}{AICl{BBDj}}{AICl{BGBl}}}{{LGn{BGJbJH`}}}}\",[]]0Bf[\"{{{Nd{BAKl}}}{{LGn{B@EjBFLl}}}}\",[]]Bf[\"{{{Nd{BAKl}}}{{LGn{B@EnBFLl}}}}\",[]]Bn[\"{{{AICl{CBnBEKj}}LIj}{{AICl{CBnBEKj}}}}\",[]]Bn[\"{{{AICl{CBnBEKl}}LIl}{{AICl{CBnBEKl}}}}\",[]]o[\"{OHfOHf}\",[]]0000Aj[\"{{{AICl{AIOl}}}CNf}\",[]]0Ci[\"{{{AICl{CLd}}{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AINl}}}}\",[]]Cd[\"{{{AICl{CBnA@b}}{AICl{AINb}}}{{LGn{HK`JHb}}}}\",[]]Ce[\"{{{AICl{CBnAHHb}}{AICl{AINb}}}{{LGn{HK`JHb}}}}\",[]]Cd[\"{{{AICl{CBnA@d}}{AICl{AINb}}}{{LGn{HK`JHb}}}}\",[]]Cd[\"{{{AICl{CBnA@f}}{AICl{AINb}}}{{LGn{HK`JHb}}}}\",[]]Cc[\"{{{AICl{CBnO`}}{AICl{AINb}}}{{LGn{HK`JHb}}}}\",[]]Ce[\"{{{AICl{CBnABLh}}{AICl{AINb}}}{{LGn{HK`JHb}}}}\",[]]Bo[\"{{{AICl{CBnB@Kj}}BGCj}{{AICl{CBnB@Kj}}}}\",[]]Bo[\"{{{AICl{CBnB@Kn}}BGCl}{{AICl{CBnB@Kn}}}}\",[]]Aj[\"{{{AICl{BBF`}}}CIl}\",[]]Aj[\"{{{AICl{BBFb}}}CIl}\",[]]Cc[\"{{{AICl{CBnB@Ej}}{Nd{A`}}}{{AICl{CBnB@Ej}}}}\",[]]Cc[\"{{{AICl{CBnB@En}}{Nd{A`}}}{{AICl{CBnB@En}}}}\",[]]Cj[\"{{BBF`{LGd{B@Kj}}{Nl{BBDh}}{Nd{OKn}}}{{LH`{ABMj}}}}\",[]]Aj[\"{{{AICl{ABNb}}}CNf}\",[]]Aj[\"{{{AICl{ABNd}}}CNf}\",[]]Ca[\"{{{AICl{CBnAIOl}}OIjAJAh}{{LGn{HK`BGGd}}}}\",[]]Bd[\"{{{AICl{BGGl}}}{{AA`{BCFn}}}}\",[]]Bj[\"{{{AICl{AIOj}}AJCn}{{LGn{HK`JIb}}}}\",[]]Bm[\"{{{AICl{CBnAIOl}}AJCn}{{LGn{HK`JIb}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}LCl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LCl}{{AICl{CBnBCGj}}}}\",[]]Bl[\"{{}{{LGn{{LCj{BBDjBENd}}{Nl{JGn}}}}}}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]An[\"{{{AICl{ABMj}}B@Ej}HK`}\",[]]D`[\"{{{AICl{CBnAIOl}}LGj{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AIOb}}}}\",[]]o[\"{{}AICl}\",[]]Aj[\"{{{AICl{AIOj}}}LCl}\",[]]Bn[\"{{{AICl{CBnB@Kj}}CIl}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}CIl}{{AICl{CBnB@Kn}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}LIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LIl}{{AICl{CBnBCGj}}}}\",[]]:Bb[\"{{{AICl{CBnAIOl}}OdLIl}HK`}\",[]]Ak[\"{{}{{LGn{B@EjJHl}}}}\",[]]Ak[\"{{}{{LGn{B@EnJHn}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]65Cg[\"{{{AICl{CBnA@b}}{AICl{CBnAINb}}}{{LGn{HK`JHb}}}}\",[]]Ch[\"{{{AICl{CBnAHHb}}{AICl{CBnAINb}}}{{LGn{HK`JHb}}}}\",[]]Cg[\"{{{AICl{CBnA@d}}{AICl{CBnAINb}}}{{LGn{HK`JHb}}}}\",[]]Cg[\"{{{AICl{CBnA@f}}{AICl{CBnAINb}}}{{LGn{HK`JHb}}}}\",[]]Cf[\"{{{AICl{CBnO`}}{AICl{CBnAINb}}}{{LGn{HK`JHb}}}}\",[]]Ch[\"{{{AICl{CBnABLh}}{AICl{CBnAINb}}}{{LGn{HK`JHb}}}}\",[]]Bi[\"{{{AICl{CBnAHI`}}{AICl{AHI`}}}CNf}\",[]]n[\"{{}HK`}\",[]]Ce[\"{{{AICl{CBnBCGh}}{LGd{LCl}}}{{AICl{CBnBCGh}}}}\",[]]Ce[\"{{{AICl{CBnBCGj}}{LGd{LCl}}}{{AICl{CBnBCGj}}}}\",[]]Bn[\"{{{AICl{AIOj}}}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]Cd[\"{{{AICl{CBnAIOl}}OIj}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]Am[\"{{{AICl{CBnAIOn}}}HK`}\",[]]Al[\"{{{AICl{CBnJGb}}}HK`}\",[]]Am[\"{{{AICl{CBnAJ@`}}}HK`}\",[]]Al[\"{{{AICl{CBnJGd}}}HK`}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]Bj[\"{{{AICl{AIOj}}AJCn}{{LGn{HK`JIb}}}}\",[]]Bm[\"{{{AICl{CBnAIOl}}AJCn}{{LGn{HK`JIb}}}}\",[]]D`[\"{{{AICl{CBnAIOl}}LGj{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AIOb}}}}\",[]]Ce[\"{{{AICl{CBnBCGh}}{Nd{BEGb}}}{{AICl{CBnBCGh}}}}\",[]]Ce[\"{{{AICl{CBnBCGj}}{Nd{BEGd}}}{{AICl{CBnBCGj}}}}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{ABMf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{ABMf}}}{{AICl{CBnBCGj}}}}\",[]]Aa[\"{BAHdAJAf}\",[]]Aa[\"{BAHfAJAf}\",[]]9Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Am[\"{{{AICl{AIOj}}LIj}HK`}\",[]]o[\"{OHfOHf}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIj}{{AICl{CBnBCGj}}}}\",[]]3109854Bn[\"{{{AICl{AIOj}}}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]Bn[\"{{{AICl{AIOl}}}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]10A`[\"{BFEbLGd}\",[]]Bm[\"{{{AICl{CBnBCGh}}Ml}{{AICl{CBnBCGh}}}}\",[]]Bm[\"{{{AICl{CBnBCGj}}Ml}{{AICl{CBnBCGj}}}}\",[]]o[\"{{}AJAh}\",[]]") \ No newline at end of file +rd_("Bf[\"{{{AICl{AODl}}{AICl{AODl}}}CNf}\",[]]Bf[\"{{{AICl{BEHj}}{AICl{BEHj}}}CNf}\",[]]Bd[\"{{{AICl{CLd}}{AICl{CLd}}}CNf}\",[]]Bf[\"{{{AICl{BBIn}}{AICl{BBIn}}}CNf}\",[]]Bf[\"{{{AICl{BBI`}}{AICl{BBI`}}}CNf}\",[]]Bf[\"{{{AICl{AHKj}}{AICl{AHKj}}}CNf}\",[]]Bf[\"{{{AICl{AJC`}}{AICl{AJC`}}}CNf}\",[]]Bf[\"{{{AICl{BBHn}}{AICl{BBHn}}}CNf}\",[]]Bf[\"{{{AICl{AJEd}}{AICl{AJEd}}}CNf}\",[]]Bf[\"{{{AICl{B@Lf}}{AICl{B@Lf}}}CNf}\",[]]Bd[\"{{{AICl{LIj}}{AICl{LIj}}}CNf}\",[]]Bf[\"{{{AICl{BFLh}}{AICl{BFLh}}}CNf}\",[]]Bd[\"{{{AICl{JFj}}{AICl{JFj}}}CNf}\",[]]Bd[\"{{{AICl{LGh}}{AICl{LGh}}}CNf}\",[]]Bf[\"{{{AICl{ACB`}}{AICl{ACB`}}}CNf}\",[]]Bf[\"{{{AICl{AHKd}}{AICl{AHKd}}}CNf}\",[]]Bd[\"{{{AICl{OMl}}{AICl{OMl}}}CNf}\",[]]Bf[\"{{{AICl{B@Ff}}{AICl{B@Ff}}}CNf}\",[]]Bf[\"{{{AICl{BCDn}}{AICl{BCDn}}}CNf}\",[]]Bf[\"{{{AICl{AOFf}}{AICl{AOFf}}}CNf}\",[]]Bf[\"{{{AICl{AOEb}}{AICl{AOEb}}}CNf}\",[]]Bf[\"{{{AICl{AHIb}}{AICl{AHIb}}}CNf}\",[]]Bf[\"{{{AICl{BCDh}}{AICl{BCDh}}}CNf}\",[]]Bf[\"{{{AICl{BDLd}}{AICl{BDLd}}}CNf}\",[]]Bf[\"{{{AICl{B@Gb}}{AICl{B@Gb}}}CNf}\",[]]Bf[\"{{{AICl{B@Gh}}{AICl{B@Gh}}}CNf}\",[]]Bf[\"{{{AICl{B@Hb}}{AICl{B@Hb}}}CNf}\",[]]Bf[\"{{{AICl{BEOn}}{AICl{BEOn}}}CNf}\",[]]Bd[\"{{{AICl{CMb}}{AICl{CMb}}}CNf}\",[]]Bb[\"{{{AICl{Ob}}{AICl{Ob}}}CNf}\",[]]Bf[\"{{{AICl{AC@h}}{AICl{AC@h}}}CNf}\",[]]Bf[\"{{{AICl{BGO`}}{AICl{BGO`}}}CNf}\",[]]Bd[\"{{{AICl{JHl}}{AICl{JHl}}}CNf}\",[]]Bd[\"{{{AICl{LIl}}{AICl{LIl}}}CNf}\",[]]Bf[\"{{{AICl{BFLj}}{AICl{BFLj}}}CNf}\",[]]Bd[\"{{{AICl{JFl}}{AICl{JFl}}}CNf}\",[]]Bf[\"{{{AICl{BEHl}}{AICl{BEHl}}}CNf}\",[]]Bd[\"{{{AICl{LGj}}{AICl{LGj}}}CNf}\",[]]Bf[\"{{{AICl{AJBd}}{AICl{AJBd}}}CNf}\",[]]Bf[\"{{{AICl{AHKb}}{AICl{AHKb}}}CNf}\",[]]Bf[\"{{{AICl{BEOh}}{AICl{BEOh}}}CNf}\",[]]Bf[\"{{{AICl{AIOb}}{AICl{AIOb}}}CNf}\",[]]Bf[\"{{{AICl{ACBb}}{AICl{ACBb}}}CNf}\",[]]Bf[\"{{{AICl{AHKf}}{AICl{AHKf}}}CNf}\",[]]Bf[\"{{{AICl{AHKl}}{AICl{AHKl}}}CNf}\",[]]Bf[\"{{{AICl{BAKf}}{AICl{BAKf}}}CNf}\",[]]Bd[\"{{{AICl{OMn}}{AICl{OMn}}}CNf}\",[]]Bf[\"{{{AICl{AJEf}}{AICl{AJEf}}}CNf}\",[]]Bf[\"{{{AICl{AOFd}}{AICl{AOFd}}}CNf}\",[]]Bf[\"{{{AICl{AOHh}}{AICl{AOHh}}}CNf}\",[]]Bf[\"{{{AICl{BBIb}}{AICl{BBIb}}}CNf}\",[]]Bf[\"{{{AICl{B@Fn}}{AICl{B@Fn}}}CNf}\",[]]Bf[\"{{{AICl{BCE`}}{AICl{BCE`}}}CNf}\",[]]Bf[\"{{{AICl{AHJl}}{AICl{AHJl}}}CNf}\",[]]Bf[\"{{{AICl{BBDn}}{AICl{BBDn}}}CNf}\",[]]Bf[\"{{{AICl{AOFh}}{AICl{AOFh}}}CNf}\",[]]Bf[\"{{{AICl{AOEd}}{AICl{AOEd}}}CNf}\",[]]Bf[\"{{{AICl{AHId}}{AICl{AHId}}}CNf}\",[]]Bf[\"{{{AICl{BBFl}}{AICl{BBFl}}}CNf}\",[]]Bf[\"{{{AICl{AJAd}}{AICl{AJAd}}}CNf}\",[]]Bf[\"{{{AICl{AJD`}}{AICl{AJD`}}}CNf}\",[]]Bf[\"{{{AICl{B@Gl}}{AICl{B@Gl}}}CNf}\",[]]Bf[\"{{{AICl{BCDl}}{AICl{BCDl}}}CNf}\",[]]Bf[\"{{{AICl{BDLh}}{AICl{BDLh}}}CNf}\",[]]Bf[\"{{{AICl{BDM`}}{AICl{BDM`}}}CNf}\",[]]Bf[\"{{{AICl{B@Gd}}{AICl{B@Gd}}}CNf}\",[]]Bf[\"{{{AICl{ABNh}}{AICl{ABNh}}}CNf}\",[]]Bf[\"{{{AICl{BEHh}}{AICl{BEHh}}}CNf}\",[]]Bf[\"{{{AICl{B@Gj}}{AICl{B@Gj}}}CNf}\",[]]Bf[\"{{{AICl{B@Hd}}{AICl{B@Hd}}}CNf}\",[]]Bf[\"{{{AICl{BBIj}}{AICl{BBIj}}}CNf}\",[]]Bf[\"{{{AICl{BF@`}}{AICl{BF@`}}}CNf}\",[]]Bd[\"{{{AICl{CMd}}{AICl{CMd}}}CNf}\",[]]Bb[\"{{{AICl{Od}}{AICl{Od}}}CNf}\",[]]Bf[\"{{{AICl{AC@j}}{AICl{AC@j}}}CNf}\",[]]Bf[\"{{{AICl{AHI`}}{AICl{AHI`}}}CNf}\",[]]Bf[\"{{{AICl{BALb}}{AICl{BALb}}}CNf}\",[]]Bf[\"{{{AICl{BFEb}}{AICl{BFEb}}}CNf}\",[]]Bd[\"{{{AICl{JI`}}{AICl{JI`}}}CNf}\",[]]Bf[\"{{{AICl{BGOb}}{AICl{BGOb}}}CNf}\",[]]Bd[\"{{{AICl{JHn}}{AICl{JHn}}}CNf}\",[]]Bf[\"{{{AICl{B@Hf}}{AICl{B@Hf}}}CNf}\",[]]Ai[\"{{{AICl{CLd}}}LGh}\",[]]Aj[\"{{{AICl{BBIn}}}LGh}\",[]]Ak[\"{{{AICl{AJBl}}}AC@h}\",[]]Ak[\"{{{AICl{BFEd}}}AC@h}\",[]]Ak[\"{{{AICl{AJCh}}}AC@h}\",[]]o[\"{LFlLGj}\",[]]A`[\"{AHFjLGj}\",[]]A`[\"{BAIdLGj}\",[]]A`[\"{B@JlLGj}\",[]]A`[\"{B@InLGj}\",[]]Aa[\"{ABOlAC@j}\",[]]Aa[\"{ACBfAC@j}\",[]]Aa[\"{ABMnAC@j}\",[]]A`[\"{OLdAC@j}\",[]]n[\"{{}OKl}\",[]]A`[\"{CKlABNh}\",[]]Be[\"{{{AICl{LIj}}{AICl{LIj}}}ABOj}\",[]]Be[\"{{{AICl{LGh}}{AICl{LGh}}}ABOj}\",[]]Bg[\"{{{AICl{AOFf}}{AICl{AOFf}}}ABOj}\",[]]Bg[\"{{{AICl{B@Gh}}{AICl{B@Gh}}}ABOj}\",[]]Be[\"{{{AICl{CMb}}{AICl{CMb}}}ABOj}\",[]]Bc[\"{{{AICl{Ob}}{AICl{Ob}}}ABOj}\",[]]Bg[\"{{{AICl{AC@h}}{AICl{AC@h}}}ABOj}\",[]]Be[\"{{{AICl{LIl}}{AICl{LIl}}}ABOj}\",[]]Bg[\"{{{AICl{BEHl}}{AICl{BEHl}}}ABOj}\",[]]Be[\"{{{AICl{LGj}}{AICl{LGj}}}ABOj}\",[]]Bg[\"{{{AICl{AHKl}}{AICl{AHKl}}}ABOj}\",[]]Bg[\"{{{AICl{BAKf}}{AICl{BAKf}}}ABOj}\",[]]Bg[\"{{{AICl{AJEf}}{AICl{AJEf}}}ABOj}\",[]]Bg[\"{{{AICl{AOFh}}{AICl{AOFh}}}ABOj}\",[]]Bg[\"{{{AICl{BDM`}}{AICl{BDM`}}}ABOj}\",[]]Bg[\"{{{AICl{BEHh}}{AICl{BEHh}}}ABOj}\",[]]Bg[\"{{{AICl{B@Gj}}{AICl{B@Gj}}}ABOj}\",[]]Be[\"{{{AICl{CMd}}{AICl{CMd}}}ABOj}\",[]]Bc[\"{{{AICl{Od}}{AICl{Od}}}ABOj}\",[]]Bg[\"{{{AICl{AC@j}}{AICl{AC@j}}}ABOj}\",[]]o[\"{AC@hOb}\",[]]o[\"{AC@jOd}\",[]]n[\"{LFnOd}\",[]]o[\"{AHGfOd}\",[]]A`[\"{ACAjLGd}\",[]]A`[\"{ACAlLGd}\",[]]A`[\"{AC@dLGd}\",[]]A`[\"{ACAnLGd}\",[]]Bh[\"{{{AICl{A@b}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AODl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBId}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BGGl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AIOf}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AIOj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BFLn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEHj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ABMj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BAH`}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABNb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJCb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBFj}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{CLd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJBl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BFEd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBI`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AHKj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJC`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBHn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Lh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOH`}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJCh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJEd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Lf}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{OLh}}{AICl{CBnAHHn}}}LGl}\",[]]Cd[\"{{{AICl{LIj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BFLh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Cd[\"{{{AICl{JFj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{LGh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{ACB`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AJ@f}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AHKd}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BCEf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{OMl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{B@Ff}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BCDn}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BCFj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BCGh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEKj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEMh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOFf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BBF`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{B@Kj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BGCj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{B@Ej}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOEb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{AHIb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BCDh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{BDLd}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{B@Gb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{B@Gh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{B@Hb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEOn}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{AJ@l}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BE@b}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{CMb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cc[\"{{{AICl{Ob}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{AC@h}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Ce[\"{{{AICl{ACAj}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cc[\"{{{AICl{Nf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AHGl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{JGb}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOEl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Cd[\"{{{AICl{OJf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BAJf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BEGf}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BGO`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{AOEh}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BF@f}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]Ce[\"{{{AICl{BFL`}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Cd[\"{{{AICl{JHl}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Bi[\"{{{AICl{AIOl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABMl}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{LIl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BFLj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{JFl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEHl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{LGj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Kb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJBd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHKb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEOh}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AIOb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ACBb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJ@h}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHKf}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCEh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHKl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BAKf}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{OMn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJEf}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AOFd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AOHh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Fn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BCE`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AHJl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBDn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BGGd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{JIf}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCFl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCGj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEKl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEMj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOFh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBFb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Kn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BGCl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@En}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOEd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AHId}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BBFl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJAd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJD`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Gl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BCDl}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BDLh}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ABLn}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BDM`}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABNd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Gd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AODh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AJCd}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BAJl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BCFh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BGGj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ABNh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEHh}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BCEd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BAGn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Gj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{B@Hd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIj}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BF@`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AJ@n}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BHCd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BE@d}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{CMd}}{AICl{CBnAHHn}}}LGl}\",[]]Bg[\"{{{AICl{Od}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{AC@j}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{ACAl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHI`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BALb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BFEb}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{JI`}}{AICl{CBnAHHn}}}LGl}\",[]]0Bi[\"{{{AICl{BGOb}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOEj}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BF@l}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BFLb}}{AICl{CBnAHHn}}}LGl}\",[]]0Cd[\"{{{AICl{JHn}}{AICl{CBnAHHn}}}{{LGn{HK`JGl}}}}\",[]]0Bg[\"{{{AICl{Nh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AHGn}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{JGd}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AOEn}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{OJh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BAJh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BEGh}}{AICl{CBnAHHn}}}LGl}\",[]]Bh[\"{{{AICl{JIb}}{AICl{CBnAHHn}}}LGl}\",[]]0Bh[\"{{{AICl{JIh}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{BBIl}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{AC@d}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{ACAn}}{AICl{CBnAHHn}}}LGl}\",[]]Bi[\"{{{AICl{B@Hf}}{AICl{CBnAHHn}}}LGl}\",[]]Ak[\"{{{AICl{B@Kb}}}ABMf}\",[]]A`[\"{ACB`CIl}\",[]]A`[\"{ACBbCIl}\",[]]Aj[\"{{{AICl{BAJl}}}LCl}\",[]]A`[\"{BGGjCIl}\",[]]o[\"{CKlCIl}\",[]]A`[\"{AC@dLCl}\",[]]Ce[\"{{{AICl{CBnBGCj}}{Nd{ACAb}}}{{AICl{CBnBGCj}}}}\",[]]Ce[\"{{{AICl{CBnBGCl}}{Nd{ACAd}}}{{AICl{CBnBGCl}}}}\",[]]8Af[\"{{ABMfCIl}BBId}\",[]]Ag[\"{{}{{Nd{BGGl}}}}\",[]]Ae[\"{{LClCIj}BAFn}\",[]]Cd[\"{{BBF`{LGd{B@Kj}}AHLb{Nd{OKn}}}{{LH`{ABMj}}}}\",[]]Ah[\"{{{Nl{JKj}}}AJBf}\",[]]A`[\"{OKfBCFj}\",[]]Ah[\"{{{Nd{OId}}}BBF`}\",[]]Bc[\"{{{Nd{B@Kl}}{Nd{BEGj}}}B@Kj}\",[]]Ai[\"{{{Nd{B@El}}}B@Ej}\",[]]B`[\"{{{AICl{{LAf{Ml}}}}}B@Gh}\",[]]Ae[\"{{CIjLCl}BE@b}\",[]]Ag[\"{{CMbObCIl}AC@h}\",[]]Aj[\"{{{Nd{AHGl}}CIh}Nf}\",[]]An[\"{{{Nd{AOEl}}OIjCIh}JGb}\",[]]An[\"{{{Nd{BAJf}}OIjCIh}OJf}\",[]]Ae[\"{{LClCIj}BAG`}\",[]]Ah[\"{{{Nl{JKj}}}AJBh}\",[]]Bm[\"{{{Nd{BBFb}}{LGd{{Nd{B@Kn}}}}CNf}ABMl}\",[]]A`[\"{LClBHFf}\",[]]o[\"{{}BHBj}\",[]]A`[\"{OKfBCFl}\",[]]Ah[\"{{{Nd{OIf}}}BBFb}\",[]]Bc[\"{{{Nd{B@L`}}{Nd{BEGl}}}B@Kn}\",[]]Ai[\"{{{Nd{B@F`}}}B@En}\",[]]Ek[\"{{ABLd{AICl{c}}{AICl{{LAf{CIj}}}}CNf}{{LGn{{LCj{BAJl{LGd{ABLd}}}}BEHh}}}{BELfJKb}}\",[\"\"]]B`[\"{{{AICl{{LAf{Ml}}}}}B@Gj}\",[]]Af[\"{{BF@`LIf}BBIj}\",[]]A`[\"{LClBHCd}\",[]]Ae[\"{{CIjLCl}BE@d}\",[]]Ag[\"{{CMdOdCIl}AC@j}\",[]]Aj[\"{{{Nd{AHGn}}CIh}Nh}\",[]]An[\"{{{Nd{AOEn}}OIjCIh}JGd}\",[]]An[\"{{{Nd{BAJh}}OIjCIh}OJh}\",[]]Aj[\"{AJEb{{LH`{BBIl}}}}\",[]]Ai[\"{{{Nd{BDOl}}}BGMd}\",[]]Ai[\"{{{Nd{BE@`}}}BGMf}\",[]]o[\"{CMbCMb}\",[]]o[\"{CMdCMd}\",[]]Ak[\"{{{AICl{AJDf}}}AJDd}\",[]]Ai[\"{{{AICl{OKn}}}OIj}\",[]]0Aj[\"{{{AICl{B@Lh}}}OIj}\",[]]Ak[\"{{{AICl{BAKn}}}AJDd}\",[]]Ak[\"{{{AICl{AJDh}}}AJDd}\",[]]Ak[\"{{{AICl{BAL`}}}AJDd}\",[]]Bg[\"{{{AICl{AIOj}}LGh}{{LGd{ABMf}}}}\",[]]Aa[\"{AHKdABMf}\",[]]Bg[\"{{{AICl{AIOl}}LGj}{{LGd{ABMf}}}}\",[]]Aa[\"{AHKfABMf}\",[]]Ah[\"{A@d{{LHb{HK`}}}}\",[]]Ah[\"{A@f{{LHb{HK`}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Aa[\"{B@InAJCn}\",[]]Aa[\"{AC@dAJCn}\",[]]Aa[\"{BBIjBF@`}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]A`[\"{ABLnJFh}\",[]]Am[\"{{{AICl{CBnABOf}}}HK`}\",[]]Am[\"{{{AICl{CBnABNb}}}HK`}\",[]]Al[\"{{{AICl{CBnCLd}}}HK`}\",[]]Am[\"{{{AICl{CBnBBIn}}}HK`}\",[]]Am[\"{{{AICl{CBnAJBl}}}HK`}\",[]]Am[\"{{{AICl{CBnAJCh}}}HK`}\",[]]Al[\"{{{AICl{CBnLEd}}}HK`}\",[]]A`[\"{cc{}}\",[\"T\"]]000000000000000000000Aa[\"{BCDnBEHj}\",[]]1111111111Aa[\"{AHKjBBI`}\",[]]22Aa[\"{AJC`AHKj}\",[]]Aa[\"{BCDnAHKj}\",[]]Aa[\"{BCDnAJC`}\",[]]55Aa[\"{AHKjBBHn}\",[]]666Aa[\"{B@LfAJEd}\",[]]Aa[\"{BCDnAJEd}\",[]]Aa[\"{B@FfAJEd}\",[]]9Aa[\"{BCDnB@Lf}\",[]]:::::::n[\"{MlLIj}\",[]]A`[\"{AC@hLIj}\",[]]A`[\"{BEOnLIj}\",[]]=o[\"{CIhLIj}\",[]]o[\"{CIjLIj}\",[]]??Al[\"{cLGh{{CKf{CIj}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]0000000A`[\"{JG`BCDn}\",[]]Aa[\"{AOE`BCDn}\",[]]A`[\"{JHfBCDn}\",[]]3333A`[\"{LIjAOFf}\",[]]44444Aa[\"{BFLhAOEb}\",[]]5Aa[\"{BCGlAOEb}\",[]]66A`[\"{JHfBCDh}\",[]]7Aa[\"{BF@fB@Gb}\",[]]888A`[\"{LIjBEOn}\",[]]99999A`[\"{LIjAC@h}\",[]]::::::::::::Aa[\"{AOHnAOEh}\",[]];;;;;;;A`[\"{BCG`JHl}\",[]]A`[\"{BGNnJHl}\",[]]A`[\"{BH@`JHl}\",[]]A`[\"{AJAjJHl}\",[]]A`[\"{BBH`JHl}\",[]]A`[\"{BDOdJHl}\",[]]A`[\"{BBFnJHl}\",[]]A`[\"{BDLjJHl}\",[]]A`[\"{BDMlJHl}\",[]]A`[\"{BCEjJHl}\",[]]A`[\"{cc{}}\",[\"T\"]]A`[\"{BGJ`JHl}\",[]]1111111111111111n[\"{MlLIl}\",[]]o[\"{CIjLIl}\",[]]A`[\"{BF@`LIl}\",[]]o[\"{CIhLIl}\",[]]5A`[\"{AC@jLIl}\",[]]666Al[\"{cLGj{{CKf{CIj}}}}\",[\"T\"]]777777777777777777Aa[\"{BBIjBCE`}\",[]]88888A`[\"{AHKbJIf}\",[]]99999A`[\"{LIlAOFh}\",[]]::::Aa[\"{BFLjAOEd}\",[]]Aa[\"{BCGlAOEd}\",[]]<<<<<<>>>>>>Aa[\"{BALbBEHh}\",[]]??????A`[\"{JI`BBIj}\",[]]A`[\"{cc{}}\",[\"T\"]]A`[\"{LIlBF@`}\",[]]11111A`[\"{LIlAC@j}\",[]]222222A`[\"{BALbJI`}\",[]]333Aa[\"{AOHnAOEj}\",[]]4444444A`[\"{BDMlJHn}\",[]]A`[\"{BH@`JHn}\",[]]A`[\"{BDOdJHn}\",[]]A`[\"{BGJ`JHn}\",[]]A`[\"{BBH`JHn}\",[]]A`[\"{BBFnJHn}\",[]]A`[\"{BGNnJHn}\",[]]A`[\"{BDLjJHn}\",[]]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ce[\"{{{Nd{B@Ef}}}{{LGn{BDOlc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Ef{{LGn{BDOlc}}}{}}\",[\"TryFrom::Error\"]]Ce[\"{{{Nd{B@Kf}}}{{LGn{BE@`c}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Kf{{LGn{BE@`c}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{LGn{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000Aj[\"{{}{{LGn{c}}}{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{A@j{{AICl{CBnc}}}}{AICl{CBnOHh}}}CLl{}}\",[\"F\"]]000000000Cc[\"{{{AICl{BBIl}}AJEb{AICl{ACAn}}}{{LH`{HK`}}}}\",[]]Bk[\"{{{AICl{BGBh}}B@Gh}{{LGn{HK`AJ@l}}}}\",[]]Bk[\"{{{AICl{BGBj}}B@Gj}{{LGn{HK`AJ@n}}}}\",[]]0Bk[\"{{{AICl{BHBj}}B@Gj}{{LGn{HK`AJ@n}}}}\",[]]Ak[\"{{{AICl{AIOj}}}ABLb}\",[]]Bi[\"{{{AICl{CBnB@Lj}}}{{LGn{LIjJHj}}}}\",[]]Bi[\"{{{AICl{CBnB@Lj}}}{{LGn{CMbJHj}}}}\",[]]Bh[\"{{{AICl{CBnB@Lj}}}{{LGn{ObJHj}}}}\",[]]Bj[\"{{{AICl{CBnB@Lj}}}{{LGn{AC@hJHj}}}}\",[]]Bf[\"{{{AICl{CBnB@Lj}}}{{LHd{LIl}}}}\",[]]Bf[\"{{{AICl{CBnB@Lj}}}{{LHd{CMd}}}}\",[]]Be[\"{{{AICl{CBnB@Lj}}}{{LHd{Od}}}}\",[]]Bg[\"{{{AICl{CBnB@Lj}}}{{LHd{AC@j}}}}\",[]]Aa[\"{AODlBFLf}\",[]]Bc[\"{{{AICl{AIOn}}}{{Nl{AIOn}}}}\",[]]Ba[\"{{{AICl{Nf}}}{{Nl{AIOn}}}}\",[]]Bb[\"{{{AICl{JGb}}}{{Nl{AIOn}}}}\",[]]Bb[\"{{{AICl{OJf}}}{{Nl{AIOn}}}}\",[]]Bc[\"{{{AICl{AJ@`}}}{{Nl{AJ@`}}}}\",[]]Ba[\"{{{AICl{Nh}}}{{Nl{AJ@`}}}}\",[]]Bb[\"{{{AICl{JGd}}}{{Nl{AJ@`}}}}\",[]]Bb[\"{{{AICl{OJh}}}{{Nl{AJ@`}}}}\",[]]A`[\"{ACB`CIl}\",[]]An[\"{{{AICl{CBnAIOl}}}AHHh}\",[]]A`[\"{ACBbCIl}\",[]]C`[\"{{{AICl{CBnB@Gh}}}{{AICl{CBn{LAf{Ml}}}}}}\",[]]C`[\"{{{AICl{CBnB@Gj}}}{{AICl{CBn{LAf{Ml}}}}}}\",[]]Ah[\"{Ml{{LGd{B@Hb}}}}\",[]]Ah[\"{Ml{{LGd{B@Hd}}}}\",[]]Ah[\"{Ml{{LGd{B@Hf}}}}\",[]]A`[\"{AC@hCMb}\",[]]A`[\"{AC@jCMd}\",[]]Bi[\"{AIOf{{LGn{{LCj{AIOjBCHb}}AIOf}}}}\",[]]o[\"{CMbCNf}\",[]]o[\"{CMdCNf}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]21Aj[\"{{{AICl{ABNb}}}CNf}\",[]]Aj[\"{{{AICl{ABNd}}}CNf}\",[]]Bn[\"{{{AICl{CBnB@Kj}}CNf}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}CNf}{{AICl{CBnB@Kn}}}}\",[]]Bl[\"{{{AICl{OKn}}OIj}{{A@j{{Nl{AINd}}}}}}\",[]]Bm[\"{{{AICl{B@Lh}}OIj}{{A@j{{Nl{AINd}}}}}}\",[]]Bm[\"{{{AICl{AOH`}}OIj}{{A@j{{Nl{AINd}}}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Ak[\"{{{AICl{AIOj}}}ABOf}\",[]]Bc[\"{{{AICl{AIOj}}AJCnAJBb}ABOh}\",[]]Ce[\"{{{AICl{CBnAIOl}}AHI`AJBdOIj}{{LGn{LGjAHJl}}}}\",[]]43Bm[\"{{{AICl{CBnAIOl}}LGj}{{LGn{HK`AIOb}}}}\",[]]E`[\"{{{A@j{{AICl{CBnBDL`}}}}{AICl{CBnOHh}}}{{CLl{{LGd{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{A@j{{AICl{CBnAJBl}}}}{AICl{CBnOHh}}{AICl{CBnOKh}}}{{CLl{{LH`{HK`}}}}}}\",[]]Ef[\"{{{A@j{{AICl{CBnAJBl}}}}{AICl{CBnOHh}}{AICl{CBn{LAf{Ml}}}}}{{CLl{{LH`{LCl}}}}}}\",[]]Ec[\"{{{AICl{CBnAJBl}}{AICl{CBnOHh}}{AICl{CBn{LAf{Ml}}}}}{{CLl{{LGn{LClAHKj}}}}}}\",[]]Fg[\"{{{AICl{CBnBBDh}}{AICl{CBnOHh}}{AICl{CBn{LAf{AJA`}}}}{AICl{CBn{LAf{AC@d}}}}}{{CLl{{LH`{LCl}}}}}}\",[]]Dn[\"{{{A@j{{AICl{CBnAHL`}}}}{AICl{ACAn}}{AICl{CBnOHh}}}{{CLl{{LH`{HK`}}}}}}\",[]];:Cd[\"{{{AICl{CBnBBF`}}{Nd{OId}}}{{AICl{CBnBBF`}}}}\",[]]Cd[\"{{{AICl{CBnBBFb}}{Nd{OIf}}}{{AICl{CBnBBFb}}}}\",[]]Cj[\"{{{AICl{B@Kl}}CIjB@Gh{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{BDOn}}CIjB@Gh{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{B@L`}}CIjB@Gj{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{BE@`}}CIjB@Gj{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Cj[\"{{{AICl{BGMf}}CIjB@Gf{AICl{{LAf{Ml}}}}}{{JKl{Ml}}}}\",[]]Bb[\"{LCl{{LCj{LCl{LGd{LCl}}}}}}\",[]]000Aj[\"{{{AICl{AIOj}}}LCl}\",[]]A`[\"{AIClLIf}\",[]]0000000000000000000000000000000000000000000000000000000000000Ce[\"{{{AICl{CBnB@Kj}}{Nd{BEGj}}}{{AICl{CBnB@Kj}}}}\",[]]Ce[\"{{{AICl{CBnB@Kn}}{Nd{BEGl}}}{{AICl{CBnB@Kn}}}}\",[]]A`[\"{BGGjJJ`}\",[]]o[\"{B@KjNd}\",[]]o[\"{B@KnNd}\",[]]Aj[\"{{{AICl{ABMj}}}HK`}\",[]]Ck[\"{{{AICl{CBnAJCh}}{AICl{{LAf{Ml}}}}}{{LGn{HK`AJEd}}}}\",[]]n[\"{{}LCl}\",[]]Ak[\"{{{AICl{AIOj}}}AHFn}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Bi[\"{{{AICl{CBn}}}{{AICl{CBnc}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bc[\"{{AICl{AICl{CBnc}}}HK`{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AICl{CBnAIOl}}OIjLGjLIl}{{LGn{HK`BBDn}}}}\",[]]A`[\"{AHHbCNf}\",[]]B`[\"{{AICl{AICl{c}}}CNf{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BCDhBEOn}\",[]]A`[\"{BDLdLIj}\",[]]Aa[\"{BCDlBF@`}\",[]]A`[\"{BDLhLIl}\",[]]o[\"{OLdLIl}\",[]]Aa[\"{BCDhAJAb}\",[]]Aa[\"{BCDlAJAd}\",[]]o[\"{LIjCIl}\",[]]o[\"{LIlCIl}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]Aj[\"{{{AICl{BAGn}}}CNf}\",[]]Bd[\"{{{AICl{BBDh}}}{{LH`{AJCn}}}}\",[]]Bd[\"{{{AICl{ABMj}}}{{LH`{AJCn}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Cb[\"{{{AICl{BCF`}}{AICl{AJCf}}CIl}{{Nd{AOHl}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Bg[\"{{{AICl{AIOj}}LGh}{{LGd{AHKd}}}}\",[]]Bj[\"{{{AICl{CBnAIOl}}LGj}{{LGd{AHKf}}}}\",[]]Aa[\"{AHFjAHKf}\",[]]Db[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}}{{CLl{{LH`{HK`}}}}}}\",[]]00Ec[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}{AICl{{LAf{Ml}}}}}{{CLl{{LH`{LCl}}}}}}\",[]]Eg[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}{AICl{{LAf{Ml}}}}}{{CLl{{LGn{LClAJEd}}}}}}\",[]]1Bm[\"{{{AICl{JKf}}{AICl{CLb}}}{{LH`{HK`}}}}\",[]]Cd[\"{{{AICl{CBnAJBl}}LCl}{{LGn{{LGd{JFj}}AHKj}}}}\",[]]Cd[\"{{{AICl{CBnBFEd}}LCl}{{LGn{{LGd{JFj}}AHKj}}}}\",[]]Cn[\"{{{AICl{CBnAJBl}}{AICl{CBn{LAf{Ml}}}}}{{LGn{HK`BBHn}}}}\",[]]Bj[\"{{{AICl{CLd}}AJBb}{{LGn{HK`BEOf}}}}\",[]]Bn[\"{{{AICl{CBnAJBf}}OIj}{{AICl{CBnAJBf}}}}\",[]]Bn[\"{{{AICl{CBnAJBh}}OIj}{{AICl{CBnAJBh}}}}\",[]]A`[\"{BBIdADn}\",[]]o[\"{JKdCIl}\",[]]o[\"{{}AJCn}\",[]]0o[\"{{}AICl}\",[]]Bj[\"{{ABNb{Nd{B@Kj}}}{{LGn{AIOfBCDn}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bh[\"{{{AICl{AHHb}}AJCn}{{LHb{AHLb}}}}\",[]]A`[\"{AHKdCIh}\",[]]Aj[\"{{{AICl{AIOl}}}CIh}\",[]]A`[\"{AHKfCIh}\",[]]Ce[\"{{{AICl{CBnAJBf}}{LGd{LIf}}}{{AICl{CBnAJBf}}}}\",[]]Ce[\"{{{AICl{CBnAJBh}}{LGd{LIf}}}{{AICl{CBnAJBh}}}}\",[]]Aa[\"{ACAjAJCn}\",[]]Aa[\"{ACAlAJCn}\",[]]Aa[\"{ACAnAJCn}\",[]]o[\"{A@bCIh}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIh}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIh}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbCIh}\",[]]Bo[\"{{{AICl{CBnBCGh}}ABMf}{{AICl{CBnBCGh}}}}\",[]]Bo[\"{{{AICl{CBnBCGj}}ABMf}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]k[\"{{}}\",[]]0000000000Bh[\"{ABNbc{}}\",[\"IntoFuture::IntoFuture\"]]11A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]o[\"{A@bLCl}\",[]]Ao[\"{{AIClc}{{OJ`{c}}}{}}\",[\"S\"]]0C`[\"{{{AICl{CBnAIOn}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]Bn[\"{{{AICl{CBnNf}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]C`[\"{{{AICl{CBnAJ@`}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]Bn[\"{{{AICl{CBnNh}}OIjCIlCNf{LGd{CIl}}}HK`}\",[]]Bk[\"{{{AICl{CBnJKf}}OIj{AICl{BCF`}}}HK`}\",[]]A`[\"{BEGfLGd}\",[]]A`[\"{BEGhLGd}\",[]]Bn[\"{{{AICl{LIj}}{AICl{LIj}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{LGh}}{AICl{LGh}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AOFf}}{AICl{AOFf}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{B@Gh}}{AICl{B@Gh}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{CMb}}{AICl{CMb}}}{{LGd{ABOj}}}}\",[]]Bl[\"{{{AICl{Ob}}{AICl{Ob}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AC@h}}{AICl{AC@h}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{LIl}}{AICl{LIl}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BEHl}}{AICl{BEHl}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{LGj}}{AICl{LGj}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AHKl}}{AICl{AHKl}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BAKf}}{AICl{BAKf}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AJEf}}{AICl{AJEf}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AOFh}}{AICl{AOFh}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BDM`}}{AICl{BDM`}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{BEHh}}{AICl{BEHh}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{B@Gj}}{AICl{B@Gj}}}{{LGd{ABOj}}}}\",[]]Bn[\"{{{AICl{CMd}}{AICl{CMd}}}{{LGd{ABOj}}}}\",[]]Bl[\"{{{AICl{Od}}{AICl{Od}}}{{LGd{ABOj}}}}\",[]]C`[\"{{{AICl{AC@j}}{AICl{AC@j}}}{{LGd{ABOj}}}}\",[]]Be[\"{{{AICl{AIOj}}}{{AC@`{AHK`}}}}\",[]]Bk[\"{{{AICl{AIOl}}LGj}{{LGn{AJBdAIOb}}}}\",[]]Df[\"{{{AICl{CBnAJBl}}{AICl{CBn{LAf{JFh}}}}}{{LGn{{LGd{LCl}}AHKj}}}}\",[]]Cc[\"{{{AICl{CBnAJBl}}LCl}{{LGn{{AA`{Ml}}BBI`}}}}\",[]]Bb[\"{{{AICl{CBnAIOl}}AC@j}AJBn}\",[]]Aj[\"{{{AICl{AHIh}}}LCl}\",[]]Aj[\"{{{AICl{AHIj}}}LCl}\",[]]Bb[\"{{{AICl{CBnAIOl}}AC@j}AJCj}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]A`[\"{BAHdLGd}\",[]]A`[\"{BAHfLGd}\",[]]Ce[\"{{{AICl{CBnB@Kj}}{Nd{AJDf}}}{{AICl{CBnB@Kj}}}}\",[]]Ce[\"{{{AICl{CBnB@Kn}}{Nd{AJDh}}}{{AICl{CBnB@Kn}}}}\",[]]Ce[\"{{{AICl{CBnB@Ej}}{Nd{AJDn}}}{{AICl{CBnB@Ej}}}}\",[]]Ce[\"{{{AICl{CBnB@En}}{Nd{AJE`}}}{{AICl{CBnB@En}}}}\",[]]o[\"{A@bCIl}\",[]]Bn[\"{{{AICl{CBnBEMh}}CIh}{{AICl{CBnBEMh}}}}\",[]]Bn[\"{{{AICl{CBnBEMj}}CIh}{{AICl{CBnBEMj}}}}\",[]]Ak[\"{{{AICl{AIOj}}}BFLn}\",[]]Aj[\"{{{AICl{CLd}}}BBIn}\",[]]Ai[\"{{{Nd{B@Kl}}}B@Kj}\",[]]Ai[\"{{{Nd{B@L`}}}B@Kn}\",[]]Aj[\"{{BCFjc}BCFjACA`}\",[\"\"]]Aj[\"{{BCFlc}BCFlACA`}\",[\"\"]]Bm[\"{{{AICl{CBnAJCh}}JFh}{{LGn{HK`AJEd}}}}\",[]]n[\"{{}LFh}\",[]]o[\"{OHfOHf}\",[]]0000Bd[\"{{{AICl{BGBh}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{BGBj}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{BHBj}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{BHCd}}}{{LGd{ABMf}}}}\",[]]Bd[\"{{{AICl{AIOj}}}{{LGd{BCDn}}}}\",[]]Ck[\"{{{AICl{ABMj}}B@EjAJCn{AICl{CIf}}}{{LGn{AIOfB@Gb}}}}\",[]]Ak[\"{{{AICl{B@Kb}}}ABMf}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]An[\"{{AJBjCNf}{{LHb{LCl}}}}\",[]]Cj[\"{{{AICl{OL`}}}{{LGd{{LCj{{Nl{AHIh}}{Nl{AHJf}}}}}}}}\",[]]Ck[\"{{{AICl{AJDj}}}{{LGd{{LCj{{Nl{AHIh}}{Nl{AHJf}}}}}}}}\",[]]Cj[\"{{{AICl{OLb}}}{{LGd{{LCj{{Nl{AHIj}}{Nl{AHJh}}}}}}}}\",[]]Ck[\"{{{AICl{AJDl}}}{{LGd{{LCj{{Nl{AHIj}}{Nl{AHJh}}}}}}}}\",[]]A`[\"{BFKbAA`}\",[]]A`[\"{BHFhAA`}\",[]]A`[\"{BFKdAA`}\",[]]A`[\"{BHFjAA`}\",[]]An[\"{{{AICl{CBnBGBh}}}B@Gh}\",[]]An[\"{{{AICl{CBnBGBj}}}B@Gj}\",[]]An[\"{{{AICl{CBnBHBj}}}B@Gj}\",[]]An[\"{{{AICl{CBnBHCd}}}B@Gj}\",[]]Ak[\"{{{AICl{BBIl}}}B@Ih}\",[]]Ba[\"{{{AICl{CBnAIOl}}BCEd}HK`}\",[]]Bo[\"{{{AICl{CBnABMl}}BDM`BAGn}{{LGd{BCEd}}}}\",[]]B`[\"{{{AICl{OL`}}B@GhCMb}CKh}\",[]]Bn[\"{{{AICl{B@Kl}}CIjB@Gh}{{LGn{CKhBF@f}}}}\",[]]Ba[\"{{{AICl{AJDj}}B@GhCMb}CKh}\",[]]Bn[\"{{{AICl{BDOn}}CIjB@Gh}{{LGn{CKhBF@f}}}}\",[]]B`[\"{{{AICl{OLb}}B@GjCMd}CKj}\",[]]Bn[\"{{{AICl{B@L`}}CIjB@Gj}{{LGn{CKjBF@l}}}}\",[]]Ba[\"{{{AICl{AJDl}}B@GjCMd}CKj}\",[]]Bn[\"{{{AICl{BE@`}}CIjB@Gj}{{LGn{CKjBF@l}}}}\",[]]Bn[\"{{{AICl{BGMf}}CIjB@Gf}{{LGn{CKjBF@l}}}}\",[]]Aj[\"{{}{{OKj{c}}}{}}\",[\"T\"]]0A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Bn[\"{{{AICl{CBnB@Kj}}LCl}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}LCl}{{AICl{CBnB@Kn}}}}\",[]]Aj[\"{{{AICl{BBDh}}}CNf}\",[]]0Aj[\"{{{AICl{BBIl}}}CNf}\",[]]Bk[\"{{{AICl{AIOl}}LGj}{{LGn{AHI`AIOb}}}}\",[]]Cb[\"{{{AICl{BCF`}}{AICl{AJBj}}CIl}{{Nd{AOHl}}}}\",[]]Ak[\"{{{AICl{ABNb}}}B@Gh}\",[]]Ak[\"{{{AICl{ABNd}}}B@Gj}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bf[\"{{{AICl{CBnAIOl}}}{{LGd{OIj}}}}\",[]]Ce[\"{{{AICl{CBnBCGh}}{Nd{AOGd}}}{{AICl{CBnBCGh}}}}\",[]]Ce[\"{{{AICl{CBnBCGj}}{Nd{AOGf}}}{{AICl{CBnBCGj}}}}\",[]]43A`[\"{ACAjLGd}\",[]]A`[\"{ACAlLGd}\",[]]A`[\"{ACAnLGd}\",[]]Ai[\"{{{AICl{OM`}}}LCl}\",[]]Bo[\"{{{AICl{CBnBHBj}}ABMf}{{AICl{CBnBHBj}}}}\",[]]Bo[\"{{{AICl{CBnBHCd}}ABMf}{{AICl{CBnBHCd}}}}\",[]]Bj[\"{{{AICl{AJCh}}CAh}{{LGn{HK`B@Ff}}}}\",[]]Bm[\"{{{AICl{CBnAJCj}}CAh}{{LGn{HK`B@Fn}}}}\",[]]<;A`[\"{JKdAOHj}\",[]]A`[\"{AODnJKd}\",[]]Bi[\"{{{Nd{B@Ef}}JKh}{{LGn{BDOjBFL`}}}}\",[]]Bi[\"{{{Nd{B@Kf}}JKh}{{LGn{BDOnBFL`}}}}\",[]]Bi[\"{{{Nd{B@Ef}}JKh}{{LGn{BDOlBFLb}}}}\",[]]Bi[\"{{{Nd{B@Kf}}JKh}{{LGn{BE@`BFLb}}}}\",[]]Co[\"{{{AICl{CBnAJCh}}{AICl{CBn{LAf{JFh}}}}}{{LGn{OMlAJEd}}}}\",[]]Co[\"{{{AICl{CBnAJCj}}{AICl{CBn{LAf{JFh}}}}}{{LGn{OMnAJEf}}}}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]A`[\"{AHHbCNf}\",[]]E`[\"{{{AICl{CBnBBF`}}c}{{AICl{CBnBBF`}}}{{A`{}{{LGf{{Nl{BGBh}}}}}}CM`CMf}}\",[\"F\"]]E`[\"{{{AICl{CBnBBFb}}c}{{AICl{CBnBBFb}}}{{A`{}{{LGf{{Nl{BGBj}}}}}}CM`CMf}}\",[\"F\"]]Bj[\"{{{AICl{BBDh}}}{{A@j{{Nl{AHL`}}}}}}\",[]]o[\"{A@bCIl}\",[]]Aa[\"{AOHjAHIl}\",[]]76Aa[\"{AJCbABNb}\",[]]Aa[\"{AJCdABNd}\",[]]Cf[\"{{{AICl{CBnBEKj}}{LGd{ABMf}}}{{AICl{CBnBEKj}}}}\",[]]Cf[\"{{{AICl{CBnBEKl}}{LGd{ABMf}}}{{AICl{CBnBEKl}}}}\",[]]9;:An[\"{{{AICl{AOHl}}ABMf}HK`}\",[]]B`[\"{{{AICl{CBnAIOn}}CIh}HK`}\",[]]An[\"{{{AICl{CBnNf}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnJGb}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnOJf}}CIh}HK`}\",[]]B`[\"{{{AICl{CBnAJ@`}}CIh}HK`}\",[]]An[\"{{{AICl{CBnNh}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnJGd}}CIh}HK`}\",[]]Ao[\"{{{AICl{CBnOJh}}CIh}HK`}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Bh[\"{{{AICl{AIOj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OL`}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OLb}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDl}}}{{LGd{{Nl{Nb}}}}}}\",[]]Dm[\"{{{AICl{CBnAJBl}}{AICl{CBnOHh}}{AICl{CBnOKh}}}{{CLl{{LGn{HK`AHKj}}}}}}\",[]]Db[\"{{{A@j{{AICl{CBnAJCh}}}}{AICl{CBnOHh}}}{{CLl{{LH`{HK`}}}}}}\",[]]Db[\"{{{AICl{CBnAIOl}}OIjB@Ih{AICl{CBn{AA`{Ml}}}}}{{LGd{ACAl}}}}\",[]]Cf[\"{{{AICl{CBnBCGh}}{AICl{CIf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{AICl{CIf}}}{{AICl{CBnBCGj}}}}\",[]]Ak[\"{{{AICl{AIOj}}}B@Jj}\",[]]Bj[\"{{{AICl{AIOj}}JFh}{{LGn{HK`BEHj}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Ai[\"{{{AICl{OId}}}LCl}\",[]]Ai[\"{{{AICl{OIf}}}LCl}\",[]]De[\"{{{Nd{B@El}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OL`}}B@Gb}}}}\",[]]Bo[\"{{{Nd{B@Kl}}CIj{AICl{BFEb}}}{{Nl{OL`}}}}\",[]]De[\"{{{Nd{BDOj}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OL`}}B@Gb}}}}\",[]]Bo[\"{{{Nd{BDOn}}CIj{AICl{BFEb}}}{{Nl{OL`}}}}\",[]]De[\"{{{Nd{B@F`}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OLb}}B@Gd}}}}\",[]]Bo[\"{{{Nd{B@L`}}CIj{AICl{BFEb}}}{{Nl{OLb}}}}\",[]]De[\"{{{Nd{BDOl}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OLb}}B@Gd}}}}\",[]]Bo[\"{{{Nd{BE@`}}CIj{AICl{BFEb}}}{{Nl{OLb}}}}\",[]]De[\"{{{Nd{BGMd}}CIj{AICl{CIf}}{AICl{BFEb}}}{{LGn{{Nl{OLb}}B@G`}}}}\",[]]Bo[\"{{{Nd{BGMf}}CIj{AICl{BFEb}}}{{Nl{OLb}}}}\",[]]Cc[\"{{{AICl{BEGj}}{AICl{{LAf{Ml}}}}}{{Nl{OGl}}}}\",[]]Cc[\"{{{AICl{BEGl}}{AICl{{LAf{Ml}}}}}{{Nl{OGn}}}}\",[]]Bn[\"{{AJCnBBDjA@b}{{LHb{{LCj{ABMhAIOh}}}}}}\",[]]Bd[\"{{{AICl{AIOl}}}{{AICl{OLb}}}}\",[]]A`[\"{AODnJKd}\",[]]Cb[\"{{{AICl{AOGd}}CMbAJCnB@GhOIj}{{LGd{AJBf}}}}\",[]]Cb[\"{{{AICl{BCFj}}CMbAJCnB@GhOIj}{{LGd{AJBf}}}}\",[]]Cb[\"{{{AICl{AOGf}}CMdAJCnB@GjOIj}{{LGd{AJBh}}}}\",[]]Cb[\"{{{AICl{BCFl}}CMdAJCnB@GjOIj}{{LGd{AJBh}}}}\",[]]B`[\"{{{AICl{CBnAIOl}}OIj}HK`}\",[]]Bo[\"{{{AICl{CBnAIOf}}}{{LGn{{Nl{Nb}}BCDn}}}}\",[]]Bh[\"{{{AICl{AIOj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OL`}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDj}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bg[\"{{{AICl{OLb}}}{{LGd{{Nl{Nb}}}}}}\",[]]Bh[\"{{{AICl{AJDl}}}{{LGd{{Nl{Nb}}}}}}\",[]]o[\"{AJ@fMl}\",[]]o[\"{AJ@hMl}\",[]]Ai[\"{{{AICl{CIf}}}CNf}\",[]]Aj[\"{{{AICl{AIOn}}}CIl}\",[]]Ah[\"{{{AICl{Nf}}}CIl}\",[]]Bn[\"{{{AICl{CBnAHGl}}CIl}{{AICl{CBnAHGl}}}}\",[]]Ai[\"{{{AICl{JGb}}}CIl}\",[]]Bn[\"{{{AICl{CBnAOEl}}CIl}{{AICl{CBnAOEl}}}}\",[]]Ai[\"{{{AICl{OJf}}}CIl}\",[]]Bn[\"{{{AICl{CBnBAJf}}CIl}{{AICl{CBnBAJf}}}}\",[]]Aj[\"{{{AICl{AJ@`}}}CIl}\",[]]Ah[\"{{{AICl{Nh}}}CIl}\",[]]Bn[\"{{{AICl{CBnAHGn}}CIl}{{AICl{CBnAHGn}}}}\",[]]Ai[\"{{{AICl{JGd}}}CIl}\",[]]Bn[\"{{{AICl{CBnAOEn}}CIl}{{AICl{CBnAOEn}}}}\",[]]Ai[\"{{{AICl{OJh}}}CIl}\",[]]Bn[\"{{{AICl{CBnBAJh}}CIl}{{AICl{CBnBAJh}}}}\",[]]Aa[\"{AJBlBFEd}\",[]]Ai[\"{{{AICl{OL`}}}CNf}\",[]]Aj[\"{{{AICl{AJDj}}}CNf}\",[]]Aj[\"{{{AICl{AIOl}}}CNf}\",[]]Ai[\"{{{AICl{OLb}}}CNf}\",[]]Aj[\"{{{AICl{AJDl}}}CNf}\",[]]Co[\"{{{AICl{OL`}}B@Gh{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]D`[\"{{{AICl{AJDj}}B@Gh{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]Co[\"{{{AICl{OLb}}B@Gj{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]D`[\"{{{AICl{AJDl}}B@Gj{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}CNf}\",[]]Bn[\"{{{AICl{CBnBEMh}}CIh}{{AICl{CBnBEMh}}}}\",[]]Bn[\"{{{AICl{CBnBEMj}}CIh}{{AICl{CBnBEMj}}}}\",[]]Cc[\"{{{AICl{CBnOL`}}}{{LGd{{OIl{{Nl{AHJf}}}}}}}}\",[]]Cd[\"{{{AICl{CBnAJDj}}}{{LGd{{OIl{{Nl{AHJf}}}}}}}}\",[]]Cc[\"{{{AICl{CBnOLb}}}{{LGd{{OIn{{Nl{AHJh}}}}}}}}\",[]]Cd[\"{{{AICl{CBnAJDl}}}{{LGd{{OIn{{Nl{AHJh}}}}}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]De[\"{{{AICl{CBnBCGh}}c{AICl{CIf}}}{{AICl{CBnBCGh}}}{{JFf{CLb}}}}\",[\"\"]]De[\"{{{AICl{CBnBCGj}}c{AICl{CIf}}}{{AICl{CBnBCGj}}}{{JFf{CLb}}}}\",[\"\"]]Ci[\"{{{AICl{CBnOL`}}{AICl{{LAf{Ml}}}}}{{LGn{CNfJHf}}}}\",[]]Cj[\"{{{AICl{CBnAJDj}}{AICl{{LAf{Ml}}}}}{{LGn{CNfJHf}}}}\",[]]Cj[\"{{{AICl{CBnOLb}}{AICl{{LAf{Ml}}}}}{{LGn{CNfBBIj}}}}\",[]]Ck[\"{{{AICl{CBnAJDl}}{AICl{{LAf{Ml}}}}}{{LGn{CNfBBIj}}}}\",[]]o[\"{A@bCNf}\",[]]Bn[\"{{{AICl{CBnBCGh}}LIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LIl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]Ca[\"{{{AICl{CBnAJBl}}}{{LGn{{LGd{LIj}}AJC`}}}}\",[]]Ca[\"{{{AICl{CBnBFEd}}}{{LGn{{LGd{LIj}}AJC`}}}}\",[]]Ca[\"{{{AICl{CBnAJBn}}}{{LGn{{LGd{LIl}}B@Fn}}}}\",[]]Ak[\"{{{AICl{AIOf}}}AJCn}\",[]]Ak[\"{{{AICl{AIOj}}}AJCn}\",[]]Ak[\"{{{AICl{ABNb}}}AJCn}\",[]]Bg[\"{{{AICl{CLd}}}{{LGn{AJCnAINl}}}}\",[]]Ak[\"{{{AICl{ABNd}}}AJCn}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]B`[\"{{LIjc}LIj{{CKf{LIj}}}}\",[\"\"]]B`[\"{{LGhc}LGh{{CKf{LGh}}}}\",[\"\"]]B`[\"{{LIlc}LIl{{CKf{LIl}}}}\",[\"\"]]B`[\"{{LGjc}LGj{{CKf{LGj}}}}\",[\"\"]]20Ae[\"{{ABMfCIl}ADn}\",[]]Bn[\"{{{AICl{CBnBCGh}}ADl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}ADl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{JKdABMf}\",[]]B`[\"{{}{{AICl{{LAf{BFDf}}}}}}\",[]]Ao[\"{{AICl{A@Mf{CBnMl}}}HK`}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{LGd{{Nd{OKn}}}}}}\",[]]Bn[\"{{{AICl{CBnBBF`}}CNf}{{AICl{CBnBBF`}}}}\",[]]Bn[\"{{{AICl{CBnBBFb}}CNf}{{AICl{CBnBBFb}}}}\",[]]Aj[\"{{{AICl{AHJf}}}CIl}\",[]]Aj[\"{{{AICl{AHJh}}}CIl}\",[]]A`[\"{AC@dLGd}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Bf[\"{{{AICl{CIf}}}{{LHb{CIlBAJj}}}}\",[]]Bm[\"{{{AICl{ABMj}}{Nl{BBDh}}}{{LH`{HK`}}}}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]Cj[\"{{{AICl{OKl}}BBDjBAKd{AICl{A@b}}}{{LCj{AJCnABMh}}}}\",[]]Cb[\"{{{AICl{CBnAIOl}}LGjAJBd}{{LGn{AJBdBEOh}}}}\",[]]Am[\"{{{AICl{AIOj}}CIl}HK`}\",[]]B`[\"{{{AICl{CBnAIOl}}CIl}HK`}\",[]]A`[\"{BBIbCNf}\",[]]B`[\"{{{AICl{CBnJKd}}BBId}HK`}\",[]]Aa[\"{AOHjAHIl}\",[]]Bm[\"{{{AICl{OKn}}AHLb}{{LH`{{Nl{BBDh}}}}}}\",[]]Bn[\"{{{AICl{B@Lh}}AHLb}{{LH`{{Nl{BBDh}}}}}}\",[]]Bn[\"{{{AICl{AOH`}}AHLb}{{LH`{{Nl{BBDh}}}}}}\",[]]Ci[\"{{{AICl{CBnOL`}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKh}}}}\",[]]Cj[\"{{{AICl{CBnAJDj}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKh}}}}\",[]]Ci[\"{{{AICl{CBnOLb}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKj}}}}\",[]]Cj[\"{{{AICl{CBnAJDl}}{AICl{CBn{AA`{Ml}}}}}{{LGd{CKj}}}}\",[]]Cb[\"{{{AICl{ACAb}}HJnAJDdABMf}{{LGn{HK`BCGb}}}}\",[]]Cb[\"{{{AICl{BAFn}}HJnAJDdABMf}{{LGn{HK`BCGb}}}}\",[]]Cb[\"{{{AICl{B@Ij}}HJnAJDdABMf}{{LGn{HK`BCGb}}}}\",[]]Cb[\"{{{AICl{ACAd}}HJnAJDdABMf}{{LGn{HK`BCGd}}}}\",[]]Cb[\"{{{AICl{BAG`}}HJnAJDdABMf}{{LGn{HK`BCGd}}}}\",[]]Cb[\"{{{AICl{B@Il}}HJnAJDdABMf}{{LGn{HK`BCGd}}}}\",[]]Al[\"{{}{{LGn{B@EhBFLb}}}}\",[]]Bm[\"{{{AICl{AIOj}}LGh}{{LGd{{Nl{AIOn}}}}}}\",[]]Bo[\"{{{AICl{AIOl}}LGj}{{LGd{{AICl{AJ@`}}}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Aj[\"{{{AICl{AIOj}}}HK`}\",[]]Am[\"{{{AICl{CBnAIOl}}}HK`}\",[]]Bf[\"{{{AICl{AINb}}}{{LGn{A@bJHh}}}}\",[]]Bg[\"{{{AICl{AINb}}}{{LGn{AHHbJHh}}}}\",[]]Bf[\"{{{AICl{AINb}}}{{LGn{A@dJHh}}}}\",[]]Bf[\"{{{AICl{AINb}}}{{LGn{A@fJHh}}}}\",[]]Be[\"{{{AICl{AINb}}}{{LGn{O`JHh}}}}\",[]]Bg[\"{{{AICl{AINb}}}{{LGn{ABLhJHh}}}}\",[]]Ak[\"{{{AICl{BBIl}}}B@Ih}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{AOFf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{AOFh}}}{{AICl{CBnBCGj}}}}\",[]]<;Aj[\"{{{AICl{ABMj}}}LCl}\",[]]Aj[\"{{{AICl{ABMl}}}LCl}\",[]]Bc[\"{{{AICl{AIOj}}AJCnAJBb}ABOh}\",[]]Co[\"{{{AICl{CBnAIOl}}AHI`AJBdOIj}{{LGn{{LCj{LGjCNf}}AHJl}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIj}{{AICl{CBnBCGj}}}}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]Bg[\"{{{AICl{BBIl}}AJEb}{{LH`{LCl}}}}\",[]]A`[\"{AHHbCIl}\",[]]10Aj[\"{{{AICl{A@b}}}BCGf}\",[]]Ce[\"{{{AICl{CBnB@Kj}}{Nd{BCGh}}}{{AICl{CBnB@Kj}}}}\",[]]Ce[\"{{{AICl{CBnB@Ej}}{Nd{BCGh}}}{{AICl{CBnB@Ej}}}}\",[]]Ce[\"{{{AICl{CBnB@Kn}}{Nd{BCGj}}}{{AICl{CBnB@Kn}}}}\",[]]Ce[\"{{{AICl{CBnB@En}}{Nd{BCGj}}}{{AICl{CBnB@En}}}}\",[]]Aa[\"{B@KjBGCj}\",[]]Aa[\"{B@KnBGCl}\",[]]Bj[\"{{{AA`{BBDj}}BAKd}{{LGn{B@KjJHl}}}}\",[]]Bj[\"{{{AA`{BBDj}}BAKd}{{LGn{B@KnJHn}}}}\",[]]Co[\"{{{AICl{CBnAJCh}}{AICl{CBn{LAf{JFh}}}}}{{LGn{HK`AJEd}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]A`[\"{BEGfCIl}\",[]]A`[\"{BEGhCIl}\",[]]Bc[\"{{{AICl{AIOj}}}{{LGd{LCl}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Aj[\"{{{AICl{AHHh}}}LCl}\",[]]Be[\"{{{AICl{ABMj}}{LGd{B@Kj}}}HK`}\",[]]Bn[\"{{{AICl{CBnABMl}}{LGd{{Nd{B@Kn}}}}}HK`}\",[]]Bd[\"{{{AICl{BFDf}}}{{LGd{BAKb}}}}\",[]]o[\"{{}AICl}\",[]]n[\"{{}OHf}\",[]]0000Bn[\"{{{AICl{CBnBCGh}}LCl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LCl}{{AICl{CBnBCGj}}}}\",[]]o[\"{CIlLIj}\",[]]o[\"{CIlLIl}\",[]]A`[\"{BAH`CIl}\",[]]Bf[\"{{{AICl{BFLn}}{AICl{BFLn}}}CNf}\",[]]Bo[\"{{{AICl{CBnBBF`}}ABMf}{{AICl{CBnBBF`}}}}\",[]]Bo[\"{{{AICl{CBnBBFb}}ABMf}{{AICl{CBnBBFb}}}}\",[]]Ae[\"{{LClCIl}BAFn}\",[]]Ae[\"{{LClCIl}BAG`}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]7Bg[\"{{{AICl{AIOl}}LGj}{{LGd{AJBd}}}}\",[]]An[\"{{{AICl{AIOj}}JFh}B@Kd}\",[]]Am[\"{{{AICl{AIOj}}LIj}HK`}\",[]]B`[\"{{{AICl{CBnAIOl}}LIl}HK`}\",[]]Ce[\"{{{AICl{CBnBBF`}}{AA`{CIj}}}{{AICl{CBnBBF`}}}}\",[]]Ce[\"{{{AICl{CBnBBFb}}{AA`{CIj}}}{{AICl{CBnBBFb}}}}\",[]]Fg[\"{{{AICl{BGGl}}{AICl{BBDj}}{AICl{{LAf{BBDj}}}}{AICl{AJCl}}{AICl{{LAf{Ml}}}}ACBd}{{LGn{BEObJH`}}}}\",[]]Af[\"{{BCFjOIj}BCFj}\",[]]Af[\"{{BCFlOIj}BCFl}\",[]]A`[\"{BAH`CIl}\",[]]Al[\"{B@Lj{{LGn{CMbJHj}}}}\",[]]Ak[\"{B@Lj{{LGn{ObJHj}}}}\",[]]Ai[\"{B@Lj{{LHd{CMd}}}}\",[]]Ah[\"{B@Lj{{LHd{Od}}}}\",[]]o[\"{OHfOHf}\",[]]Bo[\"{{{AICl{CBnBEMh}}ABMf}{{AICl{CBnBEMh}}}}\",[]]Bo[\"{{{AICl{CBnBEMj}}ABMf}{{AICl{CBnBEMj}}}}\",[]]Bb[\"{{{AICl{OL`}}}{{LGd{CNf}}}}\",[]]Bc[\"{{{AICl{AJDj}}}{{LGd{CNf}}}}\",[]]Bb[\"{{{AICl{OLb}}}{{LGd{CNf}}}}\",[]]Bc[\"{{{AICl{AJDl}}}{{LGd{CNf}}}}\",[]]Bg[\"{{{AICl{AIOj}}}{{LGn{HK`BCDn}}}}\",[]]Ba[\"{{{AICl{BEHb}}LGhAHI`}CNf}\",[]]Ba[\"{{{AICl{BEHd}}LGjAHI`}CNf}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{ABMf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{ABMf}}}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Bl[\"{{{AICl{CBnAIOn}}OIjOIjCNfCNfCIl}HK`}\",[]]Bj[\"{{{AICl{CBnNf}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnJGb}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnOJf}}OIjOIjCNfCNfCIl}HK`}\",[]]Bl[\"{{{AICl{CBnAJ@`}}OIjOIjCNfCNfCIl}HK`}\",[]]Bj[\"{{{AICl{CBnNh}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnJGd}}OIjOIjCNfCNfCIl}HK`}\",[]]Bk[\"{{{AICl{CBnOJh}}OIjOIjCNfCNfCIl}HK`}\",[]]A`[\"{BAH`CIl}\",[]]Ak[\"{{{AICl{OM`}}Od}CIl}\",[]]Bi[\"{{{AICl{CBnAJCf}}CIl}{{LHb{HK`}}}}\",[]]<;A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]10Cf[\"{{{AICl{CBnBCGh}}{LGd{BEKj}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{BEKl}}}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHKdCIl}\",[]]A`[\"{AHKfCIl}\",[]]Bi[\"{{{AICl{CBnAINb}}}{{LGn{A@bJHh}}}}\",[]]Bj[\"{{{AICl{CBnAINb}}}{{LGn{AHHbJHh}}}}\",[]]Bi[\"{{{AICl{CBnAINb}}}{{LGn{A@dJHh}}}}\",[]]Bi[\"{{{AICl{CBnAINb}}}{{LGn{A@fJHh}}}}\",[]]Bh[\"{{{AICl{CBnAINb}}}{{LGn{O`JHh}}}}\",[]]Bj[\"{{{AICl{CBnAINb}}}{{LGn{ABLhJHh}}}}\",[]]Cc[\"{{{Nd{AIOh}}CIlCNf}{{LHb{{LCj{BBIdBBId}}}}}}\",[]]Bn[\"{{{AICl{CBnB@Kj}}CIl}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}CIl}{{AICl{CBnB@Kn}}}}\",[]]Aj[\"{{{AICl{AIOj}}}CNf}\",[]]Ak[\"{{{AICl{BBDh}}}B@Ih}\",[]]0Ci[\"{{{AICl{CBnBBF`}}CIh}{{LGn{{AICl{CBnBBF`}}AOEb}}}}\",[]]Ci[\"{{{AICl{CBnBBFb}}CIh}{{LGn{{AICl{CBnBBFb}}AOEd}}}}\",[]]A`[\"{AHHbCIh}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{BEMh}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{BEMj}}}{{AICl{CBnBCGj}}}}\",[]]Bg[\"{{{AICl{CBnAIOl}}}{{LGd{BAGn}}}}\",[]]Cf[\"{{{AICl{CBnB@Kj}}{LGd{B@Lb}}}{{AICl{CBnB@Kj}}}}\",[]]Cf[\"{{{AICl{CBnB@Kn}}{LGd{B@Lb}}}{{AICl{CBnB@Kn}}}}\",[]]Cf[\"{{{AICl{CBnB@Kj}}{LGd{B@Ld}}}{{AICl{CBnB@Kj}}}}\",[]]Cf[\"{{{AICl{CBnB@Kn}}{LGd{B@Ld}}}{{AICl{CBnB@Kn}}}}\",[]]Cc[\"{{}{{LGn{{LCj{{AA`{BBDj}}BAKd}}{Nl{JGn}}}}}}\",[]]Bn[\"{{{AICl{CBnBEKj}}LIj}{{AICl{CBnBEKj}}}}\",[]]Bn[\"{{{AICl{CBnBEKl}}LIl}{{AICl{CBnBEKl}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bo[\"{{{AICl{CBnB@Kj}}ABMf}{{AICl{CBnB@Kj}}}}\",[]]Bo[\"{{{AICl{CBnB@Kn}}ABMf}{{AICl{CBnB@Kn}}}}\",[]]Ci[\"{{{AICl{CLd}}{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AINl}}}}\",[]]Bj[\"{{{AICl{BBIl}}AJEbLCl}{{LH`{HK`}}}}\",[]]054Bm[\"{{{AICl{OL`}}}{{LGn{{LGd{BFEb}}JHf}}}}\",[]]Bn[\"{{{AICl{AJDj}}}{{LGn{{LGd{BFEb}}JHf}}}}\",[]]Bn[\"{{{AICl{OLb}}}{{LGn{{LGd{BFEb}}BBIj}}}}\",[]]Bo[\"{{{AICl{AJDl}}}{{LGn{{LGd{BFEb}}BBIj}}}}\",[]]Aj[\"{{{AICl{AHJf}}}CIl}\",[]]Aj[\"{{{AICl{AHJh}}}CIl}\",[]]Bk[\"{{{AICl{ABMj}}{LGd{{Nd{BEHb}}}}}HK`}\",[]]Cc[\"{{{AICl{CBnAIOl}}{LGd{{AICl{BEHd}}}}OIj}HK`}\",[]]Aj[\"{{{AICl{ABMl}}}CIl}\",[]]Bf[\"{{{AICl{AHI`}}{AICl{AHI`}}}CNf}\",[]]Bn[\"{{{AICl{CBnBAJf}}ADl}{{AICl{CBnBAJf}}}}\",[]]Bn[\"{{{AICl{CBnBAJh}}ADl}{{AICl{CBnBAJh}}}}\",[]]Ak[\"{{{AICl{AHL`}}}B@Ih}\",[]]0Bd[\"{{{AICl{AIOj}}}{{AC@`{JIh}}}}\",[]]Cb[\"{{{AICl{AIOl}}LGj}{{LGn{{LGd{AJCn}}AIOb}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]Bn[\"{{{AICl{CBnBCGh}}LIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LIl}{{AICl{CBnBCGj}}}}\",[]]A`[\"{AHHbLGd}\",[]]Bo[\"{{{AICl{AHHb}}{AICl{CIf}}}{{LH`{BCGf}}}}\",[]]Em[\"{{{AICl{AIOj}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGO`}}}}\",[]]El[\"{{{AICl{OL`}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGO`}}}}\",[]]Em[\"{{{AICl{AJDj}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGO`}}}}\",[]]El[\"{{{AICl{OLb}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGOb}}}}\",[]]Em[\"{{{AICl{AJDl}}{AICl{CBn{LAf{Ml}}}}{AICl{{LAf{Ml}}}}{AICl{{LAf{Ml}}}}}{{LGn{HK`BGOb}}}}\",[]]Am[\"{{{AICl{CBnAHHf}}}HK`}\",[]]0Al[\"{{{AICl{AIOl}}Od}CIl}\",[]]Bl[\"{{{AICl{AIOj}}}{{AC@b{{LGd{AJCn}}}}}}\",[]]Bg[\"{{{AICl{CLd}}}{{LGn{BDL`AINl}}}}\",[]]>=E`[\"{{{AICl{BGGl}}{AICl{{LAf{Ml}}}}{AICl{BBDj}}{AICl{BGBl}}}{{LGn{BGJbJH`}}}}\",[]]0Bf[\"{{{Nd{BAKl}}}{{LGn{B@EjBFLl}}}}\",[]]Bf[\"{{{Nd{BAKl}}}{{LGn{B@EnBFLl}}}}\",[]]Bn[\"{{{AICl{CBnBEKj}}LIj}{{AICl{CBnBEKj}}}}\",[]]Bn[\"{{{AICl{CBnBEKl}}LIl}{{AICl{CBnBEKl}}}}\",[]]o[\"{OHfOHf}\",[]]0000Aj[\"{{{AICl{AIOl}}}CNf}\",[]]0Ci[\"{{{AICl{CLd}}{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AINl}}}}\",[]]Cd[\"{{{AICl{CBnA@b}}{AICl{AINb}}}{{LGn{HK`JHh}}}}\",[]]Ce[\"{{{AICl{CBnAHHb}}{AICl{AINb}}}{{LGn{HK`JHh}}}}\",[]]Cd[\"{{{AICl{CBnA@d}}{AICl{AINb}}}{{LGn{HK`JHh}}}}\",[]]Cd[\"{{{AICl{CBnA@f}}{AICl{AINb}}}{{LGn{HK`JHh}}}}\",[]]Cc[\"{{{AICl{CBnO`}}{AICl{AINb}}}{{LGn{HK`JHh}}}}\",[]]Ce[\"{{{AICl{CBnABLh}}{AICl{AINb}}}{{LGn{HK`JHh}}}}\",[]]Bo[\"{{{AICl{CBnB@Kj}}BGCj}{{AICl{CBnB@Kj}}}}\",[]]Bo[\"{{{AICl{CBnB@Kn}}BGCl}{{AICl{CBnB@Kn}}}}\",[]]Aj[\"{{{AICl{BBF`}}}CIl}\",[]]Aj[\"{{{AICl{BBFb}}}CIl}\",[]]Cc[\"{{{AICl{CBnB@Ej}}{Nd{A`}}}{{AICl{CBnB@Ej}}}}\",[]]Cc[\"{{{AICl{CBnB@En}}{Nd{A`}}}{{AICl{CBnB@En}}}}\",[]]Cj[\"{{BBF`{LGd{B@Kj}}{Nl{BBDh}}{Nd{OKn}}}{{LH`{ABMj}}}}\",[]]Aj[\"{{{AICl{ABNb}}}CNf}\",[]]Aj[\"{{{AICl{ABNd}}}CNf}\",[]]Ca[\"{{{AICl{CBnAIOl}}OIjAJAh}{{LGn{HK`BGGd}}}}\",[]]Bd[\"{{{AICl{BGGl}}}{{AA`{BCFn}}}}\",[]]Bj[\"{{{AICl{AIOj}}AJCn}{{LGn{HK`JIb}}}}\",[]]Bm[\"{{{AICl{CBnAIOl}}AJCn}{{LGn{HK`JIb}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}LCl}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LCl}{{AICl{CBnBCGj}}}}\",[]]Bl[\"{{}{{LGn{{LCj{BBDjBENd}}{Nl{JGn}}}}}}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]A`[\"{AJ@fCIl}\",[]]A`[\"{AJ@hCIl}\",[]]An[\"{{{AICl{ABMj}}B@Ej}HK`}\",[]]D`[\"{{{AICl{CBnAIOl}}LGj{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AIOb}}}}\",[]]o[\"{{}AICl}\",[]]Aj[\"{{{AICl{AIOj}}}LCl}\",[]]Bn[\"{{{AICl{CBnB@Kj}}CIl}{{AICl{CBnB@Kj}}}}\",[]]Bn[\"{{{AICl{CBnB@Kn}}CIl}{{AICl{CBnB@Kn}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}LIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}LIl}{{AICl{CBnBCGj}}}}\",[]]:Bb[\"{{{AICl{CBnAIOl}}OdLIl}HK`}\",[]]Ak[\"{{}{{LGn{B@EjJHl}}}}\",[]]Ak[\"{{}{{LGn{B@EnJHn}}}}\",[]]Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]65Cg[\"{{{AICl{CBnA@b}}{AICl{CBnAINb}}}{{LGn{HK`JHh}}}}\",[]]Ch[\"{{{AICl{CBnAHHb}}{AICl{CBnAINb}}}{{LGn{HK`JHh}}}}\",[]]Cg[\"{{{AICl{CBnA@d}}{AICl{CBnAINb}}}{{LGn{HK`JHh}}}}\",[]]Cg[\"{{{AICl{CBnA@f}}{AICl{CBnAINb}}}{{LGn{HK`JHh}}}}\",[]]Cf[\"{{{AICl{CBnO`}}{AICl{CBnAINb}}}{{LGn{HK`JHh}}}}\",[]]Ch[\"{{{AICl{CBnABLh}}{AICl{CBnAINb}}}{{LGn{HK`JHh}}}}\",[]]Bi[\"{{{AICl{CBnAHI`}}{AICl{AHI`}}}CNf}\",[]]n[\"{{}HK`}\",[]]Ce[\"{{{AICl{CBnBCGh}}{LGd{LCl}}}{{AICl{CBnBCGh}}}}\",[]]Ce[\"{{{AICl{CBnBCGj}}{LGd{LCl}}}{{AICl{CBnBCGj}}}}\",[]]Bn[\"{{{AICl{AIOj}}}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]Cd[\"{{{AICl{CBnAIOl}}OIj}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]Am[\"{{{AICl{CBnAIOn}}}HK`}\",[]]Al[\"{{{AICl{CBnJGb}}}HK`}\",[]]Am[\"{{{AICl{CBnAJ@`}}}HK`}\",[]]Al[\"{{{AICl{CBnJGd}}}HK`}\",[]]Ai[\"{AIOh{{LHb{HK`}}}}\",[]]Bj[\"{{{AICl{AIOj}}AJCn}{{LGn{HK`JIb}}}}\",[]]Bm[\"{{{AICl{CBnAIOl}}AJCn}{{LGn{HK`JIb}}}}\",[]]D`[\"{{{AICl{CBnAIOl}}LGj{LGd{ABMf}}}{{LGn{{LGd{ABMf}}AIOb}}}}\",[]]Ce[\"{{{AICl{CBnBCGh}}{Nd{BEGb}}}{{AICl{CBnBCGh}}}}\",[]]Ce[\"{{{AICl{CBnBCGj}}{Nd{BEGd}}}{{AICl{CBnBCGj}}}}\",[]]Cf[\"{{{AICl{CBnBCGh}}{LGd{ABMf}}}{{AICl{CBnBCGh}}}}\",[]]Cf[\"{{{AICl{CBnBCGj}}{LGd{ABMf}}}{{AICl{CBnBCGj}}}}\",[]]Aa[\"{BAHdAJAf}\",[]]Aa[\"{BAHfAJAf}\",[]]9Bn[\"{{{AICl{CBnBCGh}}CNf}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CNf}{{AICl{CBnBCGj}}}}\",[]]Am[\"{{{AICl{AIOj}}LIj}HK`}\",[]]o[\"{OHfOHf}\",[]]Bn[\"{{{AICl{CBnBCGh}}CIj}{{AICl{CBnBCGh}}}}\",[]]Bn[\"{{{AICl{CBnBCGj}}CIj}{{AICl{CBnBCGj}}}}\",[]]3109854Bn[\"{{{AICl{AIOj}}}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]Bn[\"{{{AICl{AIOl}}}{{LGn{{AA`{AJCn}}JIb}}}}\",[]]10A`[\"{BFEbLGd}\",[]]Bm[\"{{{AICl{CBnBCGh}}Ml}{{AICl{CBnBCGh}}}}\",[]]Bm[\"{{{AICl{CBnBCGj}}Ml}{{AICl{CBnBCGj}}}}\",[]]o[\"{{}AJAh}\",[]]") \ No newline at end of file diff --git a/pr/436/docs/search.index/path/146af92373fe.js b/pr/436/docs/search.index/path/2b02a8829606.js similarity index 98% rename from pr/436/docs/search.index/path/146af92373fe.js rename to pr/436/docs/search.index/path/2b02a8829606.js index 9ff26c874..019bb2be0 100644 --- a/pr/436/docs/search.index/path/146af92373fe.js +++ b/pr/436/docs/search.index/path/2b02a8829606.js @@ -1 +1 @@ -rd_("f[1,\"\"]0Al[10,\"core::ops\",\"core::ops\"]Ce[5,\"clap_builder::util::id\",\"clap_builder::util::id\"]22A`[10,\"core::any\"]Ao[5,\"alloc::sync\",\"alloc::sync\"]Al[5,\"iroh_quinn::congestion\"]Df[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::bbr\"]Ba[5,\"alloc::boxed\",\"alloc::boxed\"]Cb[10,\"bytes::buf::buf_impl\",\"bytes::buf::buf_impl\"]Am[5,\"quinn_perf\",\"quinn_perf\"]A`[6,\"iroh_quinn\"]Bi[6,\"iroh_quinn_proto\",\"iroh_quinn_proto\"]Ae[10,\"core::ops::bit\"]Ac[5,\"bench\",\"bench\"]Ba[5,\"perf::client\",\"perf::client\"]Ba[5,\"perf::server\",\"perf::server\"]A`[10,\"core::cmp\"]Ak[5,\"core::pin\",\"core::pin\"]Am[5,\"alloc::vec\",\"alloc::vec\"]Bb[2,\"iroh_quinn_proto::congestion\"]f[1,\"\"]00Ad[2,\"iroh_quinn_udp\"]f[0,\"\"]2222Ak[10,\"clap_builder::derive\"]Bn[10,\"std::os::fd::owned\",\"std::os::fd::owned\"]Af[10,\"core::ops::drop\"]Ad[10,\"core::convert\"]Aa[10,\"core::hash\"]1Ah[5,\"iroh_quinn::crypto\"]Ci[5,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Bh[15,\"iroh_quinn_proto::ProtectedHeader\"]Ak[5,\"std::path\",\"std::path\"]Bc[5,\"iroh_quinn\",\"iroh_quinn::path\"]Bi[6,\"core::task::poll\",\"core::task::poll\"]Bb[10,\"core::marker\",\"core::marker\"]A`[6,\"iroh_quinn\"]Bi[6,\"iroh_quinn_proto\",\"iroh_quinn_proto\"]2Ah[10,\"ppv_lite86::types\"]f[1,\"\"]f[3,\"\"]A`[2,\"iroh_quinn\"]1An[2,\"iroh_quinn_proto::config\"]Bk[2,\"iroh_quinn_proto::connection::streams\"]0Ai[2,\"iroh_quinn::runtime\"]55Bd[10,\"core::convert\",\"core::convert\"]Ba[5,\"bytes::bytes\",\"bytes::bytes\"]A`[5,\"iroh_quinn\"]D`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::assembler\"]Ab[10,\"core::clone\"]Cg[6,\"iroh_quinn_proto::frame\",\"iroh_quinn_proto::frame\"]Al[5,\"iroh_quinn::congestion\"]Dh[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::cubic\"]A`[10,\"core::fmt\"]Ag[10,\"core::ops::deref\"]Ak[5,\"core::fmt\",\"core::fmt\"]Ab[10,\"core::error\"]Bc[6,\"rustls::error\",\"rustls::error\"]Ba[8,\"clap_builder\",\"clap_builder\"]B`[2,\"iroh_quinn::crypto::rustls\"]Bf[2,\"iroh_quinn_proto::crypto::rustls\"]Dk[5,\"iroh_quinn_proto::transport_error\",\"iroh_quinn_proto::transport_error\"]Bi[6,\"arbitrary::error\",\"arbitrary::error\"]B`[6,\"iroh_quinn::crypto::rustls\"]Bf[6,\"iroh_quinn_proto::crypto::rustls\"]Ee[6,\"iroh_quinn_proto::transport_parameters\",\"iroh_quinn_proto::transport_parameters\"]Ck[6,\"iroh_quinn_proto::iroh_hp\",\"iroh_quinn_proto::iroh_hp\"]Af[2,\"iroh_quinn_proto\"]Ce[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection\"]2Bi[5,\"core::ops::range\",\"core::ops::range\"]Bh[15,\"iroh_quinn_proto::ProtectedHeader\"]0Bb[10,\"core::marker\",\"core::marker\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ba[5,\"rustls::quic\",\"rustls::quic\"]Ah[10,\"std::io\",\"std::io\"]f[1,\"\"]f[3,\"\"]Bb[2,\"iroh_quinn_proto::congestion\"]l[2,\"rustls\"]A`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Ae[10,\"tonic::request\"]Aa[10,\"iroh_quinn\"]Ch[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bl[5,\"iroh_quinn\",\"iroh_quinn::runtime::smol\"]Af[2,\"iroh_quinn_proto\"]Cn[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ci[5,\"ring::error::unspecified\",\"ring::error::unspecified\"]f[3,\"\"]A`[2,\"iroh_quinn\"]Bb[2,\"iroh_quinn_proto::connection\"]1Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]?Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]A`[5,\"iroh_quinn\"]Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Cm[6,\"iroh_quinn_proto::endpoint\",\"iroh_quinn_proto::endpoint\"]A`[6,\"iroh_quinn\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ci[5,\"iroh_quinn_proto::shared\",\"iroh_quinn_proto::shared\"]5Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]4Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Be[6,\"iroh_quinn_udp\",\"iroh_quinn_udp\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]1Do[5,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]2Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]A`[5,\"iroh_quinn\"]Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Bj[6,\"iroh_quinn\",\"iroh_quinn::send_stream\"]Bm[5,\"iroh_quinn\",\"iroh_quinn::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]j[2,\"perf\"]8Cj[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Bg[5,\"iroh_quinn\",\"iroh_quinn::endpoint\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Al[5,\"iroh_quinn::congestion\"]Dk[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Dd[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ci[5,\"iroh_quinn_proto::coding\",\"iroh_quinn_proto::coding\"]Af[2,\"iroh_quinn_proto\"]0f[3,\"\"]Bg[10,\"iroh_quinn\",\"iroh_quinn::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Da[6,\"iroh_quinn_proto::connection\",\"iroh_quinn_proto::connection\"]Ce[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection\"]Ak[10,\"clap_builder::derive\"]An[15,\"iroh_quinn_proto::Event\"]:91Bg[5,\"iroh_quinn\",\"iroh_quinn::incoming\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]1Bj[6,\"iroh_quinn\",\"iroh_quinn::recv_stream\"]0Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Af[5,\"iroh_quinn_proto\"]Bj[5,\"iroh_quinn_udp\",\"iroh_quinn_udp::imp\"]Bc[5,\"iroh_quinn\",\"iroh_quinn::path\"]f[3,\"\"]A`[5,\"iroh_quinn\"]:A`[6,\"iroh_quinn\"]?Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]2Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]7Cg[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Do[5,\"iroh_quinn_proto::config::transport\",\"iroh_quinn_proto::config::transport\"]A`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Ae[10,\"tonic::request\"]Aa[10,\"iroh_quinn\"]Ch[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bl[5,\"iroh_quinn\",\"iroh_quinn::runtime::smol\"]Af[2,\"iroh_quinn_proto\"]Cn[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ci[5,\"ring::error::unspecified\",\"ring::error::unspecified\"]f[3,\"\"]A`[2,\"iroh_quinn\"]Bb[2,\"iroh_quinn_proto::connection\"]1Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]?Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]A`[5,\"iroh_quinn\"]Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Cm[6,\"iroh_quinn_proto::endpoint\",\"iroh_quinn_proto::endpoint\"]A`[6,\"iroh_quinn\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ci[5,\"iroh_quinn_proto::shared\",\"iroh_quinn_proto::shared\"]5Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]4Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Be[6,\"iroh_quinn_udp\",\"iroh_quinn_udp\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]1Do[5,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]2Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]A`[5,\"iroh_quinn\"]Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Bj[6,\"iroh_quinn\",\"iroh_quinn::send_stream\"]Bm[5,\"iroh_quinn\",\"iroh_quinn::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]j[2,\"perf\"]8Cj[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Bg[5,\"iroh_quinn\",\"iroh_quinn::endpoint\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Al[5,\"iroh_quinn::congestion\"]Dk[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Dd[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ci[5,\"iroh_quinn_proto::coding\",\"iroh_quinn_proto::coding\"]Af[2,\"iroh_quinn_proto\"]0f[3,\"\"]Bg[10,\"iroh_quinn\",\"iroh_quinn::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Da[6,\"iroh_quinn_proto::connection\",\"iroh_quinn_proto::connection\"]Ce[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection\"]Ak[10,\"clap_builder::derive\"]An[15,\"iroh_quinn_proto::Event\"]:91Bg[5,\"iroh_quinn\",\"iroh_quinn::incoming\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]1Bj[6,\"iroh_quinn\",\"iroh_quinn::recv_stream\"]0Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Af[5,\"iroh_quinn_proto\"]Bj[5,\"iroh_quinn_udp\",\"iroh_quinn_udp::imp\"]Bc[5,\"iroh_quinn\",\"iroh_quinn::path\"]f[3,\"\"]A`[5,\"iroh_quinn\"]:A`[6,\"iroh_quinn\"]?Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]2Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]7Cg[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Do[5,\"iroh_quinn_proto::config::transport\",\"iroh_quinn_proto::config::transport\"]Settings

All

Rustdoc settings

Back
\ No newline at end of file +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/436/docs/src/iroh_quinn_proto/connection/mod.rs.html b/pr/436/docs/src/iroh_quinn_proto/connection/mod.rs.html index 75bf4de1a..7dea572e2 100644 --- a/pr/436/docs/src/iroh_quinn_proto/connection/mod.rs.html +++ b/pr/436/docs/src/iroh_quinn_proto/connection/mod.rs.html @@ -25,7222 +25,7177 @@ 25 config::{ServerConfig, TransportConfig}, 26 congestion::Controller, 27 connection::{ -28 paths::AbandonState, -29 qlog::{QlogRecvPacket, QlogSink}, -30 spaces::LostPacket, -31 timer::{ConnTimer, PathTimer}, -32 }, -33 crypto::{self, Keys}, -34 frame::{ -35 self, Close, DataBlocked, Datagram, FrameStruct, NewToken, ObservedAddr, StreamDataBlocked, -36 StreamsBlocked, -37 }, -38 iroh_hp, -39 packet::{ -40 FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet, -41 PacketNumber, PartialDecode, SpaceId, -42 }, -43 range_set::ArrayRangeSet, -44 shared::{ -45 ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint, -46 EndpointEvent, EndpointEventInner, -47 }, -48 token::{ResetToken, Token, TokenPayload}, -49 transport_parameters::TransportParameters, -50}; -51 -52mod ack_frequency; -53use ack_frequency::AckFrequencyState; -54 -55mod assembler; -56pub use assembler::Chunk; -57 -58mod cid_state; -59use cid_state::CidState; -60 -61mod datagrams; -62use datagrams::DatagramState; -63pub use datagrams::{Datagrams, SendDatagramError}; -64 -65mod mtud; -66mod pacing; -67 -68mod packet_builder; -69use packet_builder::{PacketBuilder, PadDatagram}; -70 -71mod packet_crypto; -72use packet_crypto::CryptoState; -73pub(crate) use packet_crypto::EncryptionLevel; -74 -75mod paths; -76pub use paths::{ClosedPath, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError}; -77use paths::{PathData, PathState}; -78 -79pub(crate) mod qlog; -80pub(crate) mod send_buffer; -81 -82mod spaces; -83#[cfg(fuzzing)] -84pub use spaces::Retransmits; -85#[cfg(not(fuzzing))] -86use spaces::Retransmits; -87pub(crate) use spaces::SpaceKind; -88use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits}; -89 -90mod stats; -91pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats}; -92 -93mod streams; -94#[cfg(fuzzing)] -95pub use streams::StreamsState; -96#[cfg(not(fuzzing))] -97use streams::StreamsState; -98pub use streams::{ -99 Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream, -100 ShouldTransmit, StreamEvent, Streams, WriteError, Written, -101}; -102 -103mod timer; -104use timer::{Timer, TimerTable}; -105 -106mod transmit_buf; -107use transmit_buf::TransmitBuf; -108 -109mod state; -110 -111#[cfg(not(fuzzing))] -112use state::State; -113#[cfg(fuzzing)] -114pub use state::State; -115use state::StateType; -116 -117/// Protocol state and logic for a single QUIC connection -118/// -119/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application -120/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and -121/// expects timeouts through various methods. A number of simple getter methods are exposed -122/// to allow callers to inspect some of the connection state. -123/// -124/// `Connection` has roughly 4 types of methods: -125/// -126/// - A. Simple getters, taking `&self` -127/// - B. Handlers for incoming events from the network or system, named `handle_*`. -128/// - C. State machine mutators, for incoming commands from the application. For convenience we -129/// refer to this as "performing I/O" below, however as per the design of this library none of the -130/// functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and -131/// [`write`](SendStream::write), but also things like [`reset`](SendStream::reset). -132/// - D. Polling functions for outgoing events or actions for the caller to -133/// take, named `poll_*`. -134/// -135/// The simplest way to use this API correctly is to call (B) and (C) whenever -136/// appropriate, then after each of those calls, as soon as feasible call all -137/// polling methods (D) and deal with their outputs appropriately, e.g. by -138/// passing it to the application or by making a system-level I/O call. You -139/// should call the polling functions in this order: -140/// -141/// 1. [`poll_transmit`](Self::poll_transmit) -142/// 2. [`poll_timeout`](Self::poll_timeout) -143/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events) -144/// 4. [`poll`](Self::poll) -145/// -146/// Currently the only actual dependency is from (2) to (1), however additional -147/// dependencies may be added in future, so the above order is recommended. -148/// -149/// (A) may be called whenever desired. -150/// -151/// Care should be made to ensure that the input events represent monotonically -152/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout) -153/// with events of the same [`Instant`] may be interleaved in any order with a -154/// call to [`handle_event`](Self::handle_event) at that same instant; however -155/// events or timeouts with different instants must not be interleaved. -156pub struct Connection { -157 endpoint_config: Arc<EndpointConfig>, -158 config: Arc<TransportConfig>, -159 rng: StdRng, -160 /// Consolidated cryptographic state -161 crypto_state: CryptoState, -162 /// The CID we initially chose, for use during the handshake -163 handshake_cid: ConnectionId, -164 /// The CID the peer initially chose, for use during the handshake -165 remote_handshake_cid: ConnectionId, -166 /// The [`PathData`] for each path -167 /// -168 /// This needs to be ordered because [`Connection::poll_transmit`] needs to -169 /// deterministically select the next PathId to send on. -170 // TODO(flub): well does it really? But deterministic is nice for now. -171 paths: BTreeMap<PathId, PathState>, -172 /// Counter to uniquely identify every [`PathData`] created in this connection. -173 /// -174 /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all -175 /// [`PathData`]s created in the lifetime of this connection. This helps identify the -176 /// correct path when RFC9000-style migrations happen, even when they are -177 /// aborted. -178 /// -179 /// Multipath does not change this, each path can also undergo RFC9000-style -180 /// migrations. So a single multipath path ID could see several [`PathData`]s each with -181 /// their unique [`PathData::generation]. -182 path_generation_counter: u64, -183 /// Whether MTU detection is supported in this environment -184 allow_mtud: bool, -185 state: State, -186 side: ConnectionSide, -187 /// Transport parameters set by the peer -188 peer_params: TransportParameters, -189 /// Source ConnectionId of the first packet received from the peer -190 original_remote_cid: ConnectionId, -191 /// Destination ConnectionId sent by the client on the first Initial -192 initial_dst_cid: ConnectionId, -193 /// The value that the server included in the Source Connection ID field of a Retry packet, if -194 /// one was received -195 retry_src_cid: Option<ConnectionId>, -196 /// Events returned by [`Connection::poll`] -197 events: VecDeque<Event>, -198 endpoint_events: VecDeque<EndpointEventInner>, -199 /// Whether the spin bit is in use for this connection -200 spin_enabled: bool, -201 /// Outgoing spin bit state -202 spin: bool, -203 /// Packet number spaces: initial, handshake, 1-RTT -204 spaces: [PacketSpace; 3], -205 /// Highest usable packet space. -206 highest_space: SpaceKind, -207 /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted. -208 permit_idle_reset: bool, -209 /// Negotiated idle timeout -210 idle_timeout: Option<Duration>, -211 timers: TimerTable, -212 /// Number of packets received which could not be authenticated -213 authentication_failures: u64, -214 -215 // -216 // Queued non-retransmittable 1-RTT data -217 // -218 /// If the CONNECTION_CLOSE frame needs to be sent -219 connection_close_pending: bool, -220 -221 // -222 // ACK frequency -223 // -224 ack_frequency: AckFrequencyState, -225 -226 // -227 // Congestion Control -228 // -229 /// Whether the most recently received packet had an ECN codepoint set -230 receiving_ecn: bool, -231 /// Number of packets authenticated -232 total_authed_packets: u64, -233 /// Whether the last `poll_transmit` call yielded no data because there was -234 /// no outgoing application data. -235 app_limited: bool, -236 -237 // -238 // ObservedAddr -239 // -240 /// Sequence number for the next observed address frame sent to the peer. -241 next_observed_addr_seq_no: VarInt, -242 -243 streams: StreamsState, -244 /// Surplus remote CIDs for future use on new paths -245 /// -246 /// These are given out before multiple paths exist, also for paths that will never -247 /// exist. So if multipath is supported the number of paths here will be higher than -248 /// the actual number of paths in use. -249 remote_cids: FxHashMap<PathId, CidQueue>, -250 /// Attributes of CIDs generated by local endpoint -251 /// -252 /// Any path that is allowed to be opened is present in this map, as well as the already -253 /// opened paths. However since CIDs are issued async by the endpoint driver via -254 /// connection events it can not be used to know if CIDs have been issued for a path or -255 /// not. See [`Connection::max_path_id_with_cids`] for this. -256 local_cid_state: FxHashMap<PathId, CidState>, -257 /// State of the unreliable datagram extension -258 datagrams: DatagramState, -259 /// Connection level statistics -260 stats: ConnectionStats, -261 /// Path level statistics -262 path_stats: FxHashMap<PathId, PathStats>, -263 /// QUIC version used for the connection. -264 version: u32, -265 -266 // -267 // Multipath -268 // -269 /// Maximum number of concurrent paths -270 /// -271 /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even -272 /// when multipath is disabled this will be set to 1, it is not used in that case -273 /// though. -274 max_concurrent_paths: NonZeroU32, -275 /// Local maximum [`PathId`] to be used -276 /// -277 /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath -278 /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of -279 /// the highest MAX_PATH_ID frame sent. -280 /// -281 /// Any path with an ID equal or below this [`PathId`] is either: -282 /// -283 /// - Abandoned, if it is also in [`Connection::abandoned_paths`]. -284 /// - Open, in this case it is present in [`Connection::paths`] -285 /// - Not yet opened, if it is in neither of these two places. -286 /// -287 /// Note that for not-yet-open there may or may not be any CIDs issued. See -288 /// [`Connection::max_path_id_with_cids`]. -289 local_max_path_id: PathId, -290 /// Remote's maximum [`PathId`] to be used -291 /// -292 /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when -293 /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit -294 /// by sending [`Frame::MaxPathId`] frames. -295 remote_max_path_id: PathId, -296 /// The greatest [`PathId`] we have issued CIDs for -297 /// -298 /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not -299 /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued -300 /// since they are issued asynchronously by the endpoint driver. -301 max_path_id_with_cids: PathId, -302 /// The paths already abandoned -303 /// -304 /// They may still have some state left in [`Connection::paths`] or -305 /// [`Connection::local_cid_state`] since some of this has to be kept around for some -306 /// time after a path is abandoned. -307 // TODO(flub): Make this a more efficient data structure. Like ranges of abandoned -308 // paths. Or a set together with a minimum. Or something. -309 abandoned_paths: FxHashSet<PathId>, -310 -311 iroh_hp: iroh_hp::State, -312 qlog: QlogSink, -313} -314 -315/// Return value for [`Connection::poll_transmit_path`]. -316#[derive(Debug)] -317enum PollPathStatus { -318 /// Nothing to send on the path, nothing was written into the [`TransmitBuf`]. -319 NothingToSend { -320 /// If true there was data to send but congestion control did not allow so. -321 congestion_blocked: bool, -322 }, -323 /// The transmit is ready to be sent. -324 Send(Transmit), -325} -326 -327/// Return value for [`Connection::poll_transmit_path_space`]. -328#[derive(Debug)] -329enum PollPathSpaceStatus { -330 /// Nothing to send in the space, nothing was written into the [`TransmitBuf`]. -331 NothingToSend { -332 /// If true there was data to send but congestion control did not allow so. -333 congestion_blocked: bool, -334 }, -335 /// One or more packets have been written into the [`TransmitBuf`]. -336 WrotePacket { -337 /// The highest packet number. -338 last_packet_number: u64, -339 /// Whether to pad an already started datagram in the next packet. -340 /// -341 /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire -342 /// datagram they may decide to coalesce with the next packet from a higher -343 /// encryption level on the same path. But the earlier packet may require specific -344 /// size requirements for the datagram they are sent in. -345 /// -346 /// If a space did not complete the datagram, they use this to request the correct -347 /// padding in the final packet of the datagram so that the final datagram will have -348 /// the correct size. -349 /// -350 /// If a space did fill an entire datagram, it leaves this to the default of -351 /// [`PadDatagram::No`]. -352 pad_datagram: PadDatagram, -353 }, -354 /// Send the contents of the transmit immediately. -355 /// -356 /// Packets were written and the GSO batch must end now, regardless from whether higher -357 /// spaces still have frames to write. This is used when the last datagram written would -358 /// require too much padding to continue a GSO batch, which would waste space on the -359 /// wire. -360 Send { -361 /// The highest packet number written into the transmit. -362 last_packet_number: u64, -363 }, -364} -365 -366impl Connection { -367 pub(crate) fn new( -368 endpoint_config: Arc<EndpointConfig>, -369 config: Arc<TransportConfig>, -370 init_cid: ConnectionId, -371 local_cid: ConnectionId, -372 remote_cid: ConnectionId, -373 network_path: FourTuple, -374 crypto: Box<dyn crypto::Session>, -375 cid_gen: &dyn ConnectionIdGenerator, -376 now: Instant, -377 version: u32, -378 allow_mtud: bool, -379 rng_seed: [u8; 32], -380 side_args: SideArgs, -381 qlog: QlogSink, -382 ) -> Self { -383 let pref_addr_cid = side_args.pref_addr_cid(); -384 let path_validated = side_args.path_validated(); -385 let connection_side = ConnectionSide::from(side_args); -386 let side = connection_side.side(); -387 let mut rng = StdRng::from_seed(rng_seed); -388 let initial_space = PacketSpace::new(now, SpaceId::Initial, &mut rng); -389 let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng); -390 #[cfg(test)] -391 let data_space = match config.deterministic_packet_numbers { -392 true => PacketSpace::new_deterministic(now, SpaceId::Data), -393 false => PacketSpace::new(now, SpaceId::Data, &mut rng), -394 }; -395 #[cfg(not(test))] -396 let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng); -397 let state = State::handshake(state::Handshake { -398 remote_cid_set: side.is_server(), -399 expected_token: Bytes::new(), -400 client_hello: None, -401 allow_server_migration: side.is_client(), -402 }); -403 let local_cid_state = FxHashMap::from_iter([( -404 PathId::ZERO, -405 CidState::new( -406 cid_gen.cid_len(), -407 cid_gen.cid_lifetime(), -408 now, -409 if pref_addr_cid.is_some() { 2 } else { 1 }, -410 ), -411 )]); -412 -413 let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config); -414 // TODO(@divma): consider if we want to delay this until the path is validated -415 path.open = true; -416 let mut this = Self { -417 endpoint_config, -418 crypto_state: CryptoState::new(crypto, init_cid, side, &mut rng), -419 handshake_cid: local_cid, -420 remote_handshake_cid: remote_cid, -421 local_cid_state, -422 paths: BTreeMap::from_iter([( -423 PathId::ZERO, -424 PathState { -425 data: path, -426 prev: None, -427 }, -428 )]), -429 path_generation_counter: 0, -430 allow_mtud, -431 state, -432 side: connection_side, -433 peer_params: TransportParameters::default(), -434 original_remote_cid: remote_cid, -435 initial_dst_cid: init_cid, -436 retry_src_cid: None, -437 events: VecDeque::new(), -438 endpoint_events: VecDeque::new(), -439 spin_enabled: config.allow_spin && rng.random_ratio(7, 8), -440 spin: false, -441 spaces: [initial_space, handshake_space, data_space], -442 highest_space: SpaceKind::Initial, -443 permit_idle_reset: true, -444 idle_timeout: match config.max_idle_timeout { -445 None | Some(VarInt(0)) => None, -446 Some(dur) => Some(Duration::from_millis(dur.0)), -447 }, -448 timers: TimerTable::default(), -449 authentication_failures: 0, -450 connection_close_pending: false, -451 -452 ack_frequency: AckFrequencyState::new(get_max_ack_delay( -453 &TransportParameters::default(), -454 )), -455 -456 app_limited: false, -457 receiving_ecn: false, -458 total_authed_packets: 0, -459 -460 next_observed_addr_seq_no: 0u32.into(), -461 -462 streams: StreamsState::new( -463 side, -464 config.max_concurrent_uni_streams, -465 config.max_concurrent_bidi_streams, -466 config.send_window, -467 config.receive_window, -468 config.stream_receive_window, -469 ), -470 datagrams: DatagramState::default(), -471 config, -472 remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]), -473 rng, -474 stats: ConnectionStats::default(), -475 path_stats: Default::default(), -476 version, -477 -478 // peer params are not yet known, so multipath is not enabled -479 max_concurrent_paths: NonZeroU32::MIN, -480 local_max_path_id: PathId::ZERO, -481 remote_max_path_id: PathId::ZERO, -482 max_path_id_with_cids: PathId::ZERO, -483 abandoned_paths: Default::default(), -484 -485 // iroh's nat traversal -486 iroh_hp: Default::default(), -487 qlog, -488 }; -489 if path_validated { -490 this.on_path_validated(PathId::ZERO); -491 } -492 if side.is_client() { -493 // Kick off the connection -494 this.write_crypto(); -495 this.init_0rtt(now); -496 } -497 this.qlog -498 .emit_tuple_assigned(PathId::ZERO, network_path, now); -499 this -500 } -501 -502 /// Returns the next time at which `handle_timeout` should be called -503 /// -504 /// The value returned may change after: -505 /// - the application performed some I/O on the connection -506 /// - a call was made to `handle_event` -507 /// - a call to `poll_transmit` returned `Some` -508 /// - a call was made to `handle_timeout` -509 #[must_use] -510 pub fn poll_timeout(&mut self) -> Option<Instant> { -511 self.timers.peek() -512 } -513 -514 /// Returns application-facing events -515 /// -516 /// Connections should be polled for events after: -517 /// - a call was made to `handle_event` -518 /// - a call was made to `handle_timeout` -519 #[must_use] -520 pub fn poll(&mut self) -> Option<Event> { -521 if let Some(x) = self.events.pop_front() { -522 return Some(x); -523 } -524 -525 if let Some(event) = self.streams.poll() { -526 return Some(Event::Stream(event)); -527 } -528 -529 if let Some(reason) = self.state.take_error() { -530 return Some(Event::ConnectionLost { reason }); -531 } -532 -533 None -534 } -535 -536 /// Return endpoint-facing events -537 #[must_use] -538 pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> { -539 self.endpoint_events.pop_front().map(EndpointEvent) -540 } -541 -542 /// Provide control over streams -543 #[must_use] -544 pub fn streams(&mut self) -> Streams<'_> { -545 Streams { -546 state: &mut self.streams, -547 conn_state: &self.state, -548 } -549 } -550 -551 /// Provide control over streams -552 #[must_use] -553 pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> { -554 assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side()); -555 RecvStream { -556 id, -557 state: &mut self.streams, -558 pending: &mut self.spaces[SpaceId::Data].pending, -559 } -560 } -561 -562 /// Provide control over streams -563 #[must_use] -564 pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> { -565 assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side()); -566 SendStream { -567 id, -568 state: &mut self.streams, -569 pending: &mut self.spaces[SpaceId::Data].pending, -570 conn_state: &self.state, -571 } -572 } -573 -574 /// Opens a new path only if no path on the same network path currently exists. -575 /// -576 /// This comparison will use [`FourTuple::is_probably_same_path`] on the given `network_path` -577 /// and pass it existing path's network paths. -578 /// -579 /// This means that you can pass `local_ip: None` to make the comparison only compare -580 /// remote addresses. -581 /// -582 /// This avoids having to guess which local interface will be used to communicate with the -583 /// remote, should it not be known yet. We assume that if we already have a path to the remote, -584 /// the OS is likely to use the same interface to talk to said remote. -585 /// -586 /// See also [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id, -587 /// false)` if was opened. -588 /// -589 /// [`open_path`]: Connection::open_path -590 pub fn open_path_ensure( -591 &mut self, -592 network_path: FourTuple, -593 initial_status: PathStatus, -594 now: Instant, -595 ) -> Result<(PathId, bool), PathError> { -596 let existing_open_path = self.paths.iter().find(|(id, path)| { -597 network_path.is_probably_same_path(&path.data.network_path) -598 && !self.abandoned_paths.contains(*id) -599 }); -600 match existing_open_path { -601 Some((path_id, _state)) => Ok((*path_id, true)), -602 None => Ok((self.open_path(network_path, initial_status, now)?, false)), -603 } -604 } -605 -606 /// Opens a new path -607 /// -608 /// Further errors might occur and they will be emitted in [`PathEvent::LocallyClosed`] events. -609 /// When the path is opened it will be reported as an [`PathEvent::Opened`]. -610 pub fn open_path( -611 &mut self, -612 network_path: FourTuple, -613 initial_status: PathStatus, -614 now: Instant, -615 ) -> Result<PathId, PathError> { -616 if !self.is_multipath_negotiated() { -617 return Err(PathError::MultipathNotNegotiated); -618 } -619 if self.side().is_server() { -620 return Err(PathError::ServerSideNotAllowed); -621 } -622 -623 let max_abandoned = self.abandoned_paths.iter().max().copied(); -624 let max_used = self.paths.keys().last().copied(); -625 let path_id = max_abandoned -626 .max(max_used) -627 .unwrap_or(PathId::ZERO) -628 .saturating_add(1u8); -629 -630 if Some(path_id) > self.max_path_id() { -631 return Err(PathError::MaxPathIdReached); -632 } -633 if path_id > self.remote_max_path_id { -634 self.spaces[SpaceId::Data].pending.paths_blocked = true; -635 return Err(PathError::MaxPathIdReached); -636 } -637 if self -638 .remote_cids -639 .get(&path_id) -640 .map(CidQueue::active) -641 .is_none() -642 { -643 self.spaces[SpaceId::Data] -644 .pending -645 .path_cids_blocked -646 .insert(path_id); -647 return Err(PathError::RemoteCidsExhausted); -648 } -649 -650 let path = self.ensure_path(path_id, network_path, now, None); -651 path.status.local_update(initial_status); -652 -653 Ok(path_id) -654 } -655 -656 /// Closes a path by sending a PATH_ABANDON frame -657 /// -658 /// This will not allow closing the last path. It does allow closing paths which have -659 /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer -660 /// for a path that was never opened locally. -661 pub fn close_path( -662 &mut self, -663 now: Instant, -664 path_id: PathId, -665 error_code: VarInt, -666 ) -> Result<(), ClosePathError> { -667 let locally_initiated = true; -668 self.close_path_inner(now, path_id, error_code, locally_initiated) -669 } -670 -671 fn close_path_inner( -672 &mut self, -673 now: Instant, -674 path_id: PathId, -675 error_code: VarInt, -676 locally_initiated: bool, -677 ) -> Result<(), ClosePathError> { -678 if !self.is_multipath_negotiated() { -679 return Err(ClosePathError::MultipathNotNegotiated); -680 } -681 if self.abandoned_paths.contains(&path_id) -682 || Some(path_id) > self.max_path_id() -683 || !self.paths.contains_key(&path_id) -684 { -685 return Err(ClosePathError::ClosedPath); -686 } -687 -688 if locally_initiated { -689 let has_remaining_validated_paths = self.paths.iter().any(|(id, path)| { -690 *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated -691 }); -692 if !has_remaining_validated_paths { -693 return Err(ClosePathError::LastOpenPath); -694 } -695 } else { -696 // The remote abandoned this path. We should always "accept" this. Doing so right now, -697 // however, breaks assumptions throughout the code. We error instead, for the -698 // connection to be killed. See <https://github.com/n0-computer/quinn/issues/397> -699 let has_remaining_paths = self -700 .paths -701 .keys() -702 .any(|id| *id != path_id && !self.abandoned_paths.contains(id)); -703 if !has_remaining_paths { -704 return Err(ClosePathError::LastOpenPath); -705 } -706 } -707 -708 // Send PATH_ABANDON -709 self.spaces[SpaceId::Data] -710 .pending -711 .path_abandon -712 .insert(path_id, error_code.into()); -713 -714 // Remove pending NEW CIDs for this path -715 let pending_space = &mut self.spaces[SpaceId::Data].pending; -716 pending_space.new_cids.retain(|cid| cid.path_id != path_id); -717 pending_space.path_cids_blocked.retain(|&id| id != path_id); -718 pending_space.path_status.retain(|&id| id != path_id); -719 -720 // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths) -721 for space in self.spaces[SpaceId::Data].iter_paths_mut() { -722 for sent_packet in space.sent_packets.values_mut() { -723 if let Some(retransmits) = sent_packet.retransmits.get_mut() { -724 retransmits.new_cids.retain(|cid| cid.path_id != path_id); -725 retransmits.path_cids_blocked.retain(|&id| id != path_id); -726 retransmits.path_status.retain(|&id| id != path_id); -727 } -728 } -729 } -730 -731 // Consider remotely issued CIDs as retired. -732 // Technically we don't have to do this just yet. We only need to do this *after* -733 // the ABANDON_PATH frame is sent, allowing us to still send it on the -734 // to-be-abandoned path. However it is recommended to send it on another path, and -735 // we do not allow abandoning the last path anyway. -736 self.remote_cids.remove(&path_id); -737 self.endpoint_events -738 .push_back(EndpointEventInner::RetireResetToken(path_id)); -739 -740 trace!(%path_id, "abandoning path"); -741 self.abandoned_paths.insert(path_id); -742 -743 self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8)); -744 -745 // Clear all timers. -746 // We still need some timers after we close a path, e.g. the `DiscardPath` timer, -747 // but that timer is going to be set once we the `PATH_ABANDON` frame is sent. -748 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); +28 qlog::{QlogRecvPacket, QlogSink}, +29 spaces::LostPacket, +30 timer::{ConnTimer, PathTimer}, +31 }, +32 crypto::{self, Keys}, +33 frame::{ +34 self, Close, DataBlocked, Datagram, FrameStruct, NewToken, ObservedAddr, StreamDataBlocked, +35 StreamsBlocked, +36 }, +37 iroh_hp, +38 packet::{ +39 FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet, +40 PacketNumber, PartialDecode, SpaceId, +41 }, +42 range_set::ArrayRangeSet, +43 shared::{ +44 ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint, +45 EndpointEvent, EndpointEventInner, +46 }, +47 token::{ResetToken, Token, TokenPayload}, +48 transport_parameters::TransportParameters, +49}; +50 +51mod ack_frequency; +52use ack_frequency::AckFrequencyState; +53 +54mod assembler; +55pub use assembler::Chunk; +56 +57mod cid_state; +58use cid_state::CidState; +59 +60mod datagrams; +61use datagrams::DatagramState; +62pub use datagrams::{Datagrams, SendDatagramError}; +63 +64mod mtud; +65mod pacing; +66 +67mod packet_builder; +68use packet_builder::{PacketBuilder, PadDatagram}; +69 +70mod packet_crypto; +71use packet_crypto::CryptoState; +72pub(crate) use packet_crypto::EncryptionLevel; +73 +74mod paths; +75pub use paths::{ClosedPath, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError}; +76use paths::{PathData, PathState}; +77 +78pub(crate) mod qlog; +79pub(crate) mod send_buffer; +80 +81mod spaces; +82#[cfg(fuzzing)] +83pub use spaces::Retransmits; +84#[cfg(not(fuzzing))] +85use spaces::Retransmits; +86pub(crate) use spaces::SpaceKind; +87use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits}; +88 +89mod stats; +90pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats}; +91 +92mod streams; +93#[cfg(fuzzing)] +94pub use streams::StreamsState; +95#[cfg(not(fuzzing))] +96use streams::StreamsState; +97pub use streams::{ +98 Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream, +99 ShouldTransmit, StreamEvent, Streams, WriteError, Written, +100}; +101 +102mod timer; +103use timer::{Timer, TimerTable}; +104 +105mod transmit_buf; +106use transmit_buf::TransmitBuf; +107 +108mod state; +109 +110#[cfg(not(fuzzing))] +111use state::State; +112#[cfg(fuzzing)] +113pub use state::State; +114use state::StateType; +115 +116/// Protocol state and logic for a single QUIC connection +117/// +118/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application +119/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and +120/// expects timeouts through various methods. A number of simple getter methods are exposed +121/// to allow callers to inspect some of the connection state. +122/// +123/// `Connection` has roughly 4 types of methods: +124/// +125/// - A. Simple getters, taking `&self` +126/// - B. Handlers for incoming events from the network or system, named `handle_*`. +127/// - C. State machine mutators, for incoming commands from the application. For convenience we +128/// refer to this as "performing I/O" below, however as per the design of this library none of the +129/// functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and +130/// [`write`](SendStream::write), but also things like [`reset`](SendStream::reset). +131/// - D. Polling functions for outgoing events or actions for the caller to +132/// take, named `poll_*`. +133/// +134/// The simplest way to use this API correctly is to call (B) and (C) whenever +135/// appropriate, then after each of those calls, as soon as feasible call all +136/// polling methods (D) and deal with their outputs appropriately, e.g. by +137/// passing it to the application or by making a system-level I/O call. You +138/// should call the polling functions in this order: +139/// +140/// 1. [`poll_transmit`](Self::poll_transmit) +141/// 2. [`poll_timeout`](Self::poll_timeout) +142/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events) +143/// 4. [`poll`](Self::poll) +144/// +145/// Currently the only actual dependency is from (2) to (1), however additional +146/// dependencies may be added in future, so the above order is recommended. +147/// +148/// (A) may be called whenever desired. +149/// +150/// Care should be made to ensure that the input events represent monotonically +151/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout) +152/// with events of the same [`Instant`] may be interleaved in any order with a +153/// call to [`handle_event`](Self::handle_event) at that same instant; however +154/// events or timeouts with different instants must not be interleaved. +155pub struct Connection { +156 endpoint_config: Arc<EndpointConfig>, +157 config: Arc<TransportConfig>, +158 rng: StdRng, +159 /// Consolidated cryptographic state +160 crypto_state: CryptoState, +161 /// The CID we initially chose, for use during the handshake +162 handshake_cid: ConnectionId, +163 /// The CID the peer initially chose, for use during the handshake +164 remote_handshake_cid: ConnectionId, +165 /// The [`PathData`] for each path +166 /// +167 /// This needs to be ordered because [`Connection::poll_transmit`] needs to +168 /// deterministically select the next PathId to send on. +169 // TODO(flub): well does it really? But deterministic is nice for now. +170 paths: BTreeMap<PathId, PathState>, +171 /// Counter to uniquely identify every [`PathData`] created in this connection. +172 /// +173 /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all +174 /// [`PathData`]s created in the lifetime of this connection. This helps identify the +175 /// correct path when RFC9000-style migrations happen, even when they are +176 /// aborted. +177 /// +178 /// Multipath does not change this, each path can also undergo RFC9000-style +179 /// migrations. So a single multipath path ID could see several [`PathData`]s each with +180 /// their unique [`PathData::generation]. +181 path_generation_counter: u64, +182 /// Whether MTU detection is supported in this environment +183 allow_mtud: bool, +184 state: State, +185 side: ConnectionSide, +186 /// Transport parameters set by the peer +187 peer_params: TransportParameters, +188 /// Source ConnectionId of the first packet received from the peer +189 original_remote_cid: ConnectionId, +190 /// Destination ConnectionId sent by the client on the first Initial +191 initial_dst_cid: ConnectionId, +192 /// The value that the server included in the Source Connection ID field of a Retry packet, if +193 /// one was received +194 retry_src_cid: Option<ConnectionId>, +195 /// Events returned by [`Connection::poll`] +196 events: VecDeque<Event>, +197 endpoint_events: VecDeque<EndpointEventInner>, +198 /// Whether the spin bit is in use for this connection +199 spin_enabled: bool, +200 /// Outgoing spin bit state +201 spin: bool, +202 /// Packet number spaces: initial, handshake, 1-RTT +203 spaces: [PacketSpace; 3], +204 /// Highest usable packet space. +205 highest_space: SpaceKind, +206 /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted. +207 permit_idle_reset: bool, +208 /// Negotiated idle timeout +209 idle_timeout: Option<Duration>, +210 timers: TimerTable, +211 /// Number of packets received which could not be authenticated +212 authentication_failures: u64, +213 +214 // +215 // Queued non-retransmittable 1-RTT data +216 // +217 /// If the CONNECTION_CLOSE frame needs to be sent +218 connection_close_pending: bool, +219 +220 // +221 // ACK frequency +222 // +223 ack_frequency: AckFrequencyState, +224 +225 // +226 // Congestion Control +227 // +228 /// Whether the most recently received packet had an ECN codepoint set +229 receiving_ecn: bool, +230 /// Number of packets authenticated +231 total_authed_packets: u64, +232 /// Whether the last `poll_transmit` call yielded no data because there was +233 /// no outgoing application data. +234 app_limited: bool, +235 +236 // +237 // ObservedAddr +238 // +239 /// Sequence number for the next observed address frame sent to the peer. +240 next_observed_addr_seq_no: VarInt, +241 +242 streams: StreamsState, +243 /// Surplus remote CIDs for future use on new paths +244 /// +245 /// These are given out before multiple paths exist, also for paths that will never +246 /// exist. So if multipath is supported the number of paths here will be higher than +247 /// the actual number of paths in use. +248 remote_cids: FxHashMap<PathId, CidQueue>, +249 /// Attributes of CIDs generated by local endpoint +250 /// +251 /// Any path that is allowed to be opened is present in this map, as well as the already +252 /// opened paths. However since CIDs are issued async by the endpoint driver via +253 /// connection events it can not be used to know if CIDs have been issued for a path or +254 /// not. See [`Connection::max_path_id_with_cids`] for this. +255 local_cid_state: FxHashMap<PathId, CidState>, +256 /// State of the unreliable datagram extension +257 datagrams: DatagramState, +258 /// Connection level statistics +259 stats: ConnectionStats, +260 /// Path level statistics +261 path_stats: FxHashMap<PathId, PathStats>, +262 /// QUIC version used for the connection. +263 version: u32, +264 +265 // +266 // Multipath +267 // +268 /// Maximum number of concurrent paths +269 /// +270 /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even +271 /// when multipath is disabled this will be set to 1, it is not used in that case +272 /// though. +273 max_concurrent_paths: NonZeroU32, +274 /// Local maximum [`PathId`] to be used +275 /// +276 /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath +277 /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of +278 /// the highest MAX_PATH_ID frame sent. +279 /// +280 /// Any path with an ID equal or below this [`PathId`] is either: +281 /// +282 /// - Abandoned, if it is also in [`Connection::abandoned_paths`]. +283 /// - Open, in this case it is present in [`Connection::paths`] +284 /// - Not yet opened, if it is in neither of these two places. +285 /// +286 /// Note that for not-yet-open there may or may not be any CIDs issued. See +287 /// [`Connection::max_path_id_with_cids`]. +288 local_max_path_id: PathId, +289 /// Remote's maximum [`PathId`] to be used +290 /// +291 /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when +292 /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit +293 /// by sending [`Frame::MaxPathId`] frames. +294 remote_max_path_id: PathId, +295 /// The greatest [`PathId`] we have issued CIDs for +296 /// +297 /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not +298 /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued +299 /// since they are issued asynchronously by the endpoint driver. +300 max_path_id_with_cids: PathId, +301 /// The paths already abandoned +302 /// +303 /// They may still have some state left in [`Connection::paths`] or +304 /// [`Connection::local_cid_state`] since some of this has to be kept around for some +305 /// time after a path is abandoned. +306 // TODO(flub): Make this a more efficient data structure. Like ranges of abandoned +307 // paths. Or a set together with a minimum. Or something. +308 abandoned_paths: FxHashSet<PathId>, +309 +310 iroh_hp: iroh_hp::State, +311 qlog: QlogSink, +312} +313 +314/// Return value for [`Connection::poll_transmit_path`]. +315#[derive(Debug)] +316enum PollPathStatus { +317 /// Nothing to send on the path, nothing was written into the [`TransmitBuf`]. +318 NothingToSend { +319 /// If true there was data to send but congestion control did not allow so. +320 congestion_blocked: bool, +321 }, +322 /// The transmit is ready to be sent. +323 Send(Transmit), +324} +325 +326/// Return value for [`Connection::poll_transmit_path_space`]. +327#[derive(Debug)] +328enum PollPathSpaceStatus { +329 /// Nothing to send in the space, nothing was written into the [`TransmitBuf`]. +330 NothingToSend { +331 /// If true there was data to send but congestion control did not allow so. +332 congestion_blocked: bool, +333 }, +334 /// One or more packets have been written into the [`TransmitBuf`]. +335 WrotePacket { +336 /// The highest packet number. +337 last_packet_number: u64, +338 /// Whether to pad an already started datagram in the next packet. +339 /// +340 /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire +341 /// datagram they may decide to coalesce with the next packet from a higher +342 /// encryption level on the same path. But the earlier packet may require specific +343 /// size requirements for the datagram they are sent in. +344 /// +345 /// If a space did not complete the datagram, they use this to request the correct +346 /// padding in the final packet of the datagram so that the final datagram will have +347 /// the correct size. +348 /// +349 /// If a space did fill an entire datagram, it leaves this to the default of +350 /// [`PadDatagram::No`]. +351 pad_datagram: PadDatagram, +352 }, +353 /// Send the contents of the transmit immediately. +354 /// +355 /// Packets were written and the GSO batch must end now, regardless from whether higher +356 /// spaces still have frames to write. This is used when the last datagram written would +357 /// require too much padding to continue a GSO batch, which would waste space on the +358 /// wire. +359 Send { +360 /// The highest packet number written into the transmit. +361 last_packet_number: u64, +362 }, +363} +364 +365impl Connection { +366 pub(crate) fn new( +367 endpoint_config: Arc<EndpointConfig>, +368 config: Arc<TransportConfig>, +369 init_cid: ConnectionId, +370 local_cid: ConnectionId, +371 remote_cid: ConnectionId, +372 network_path: FourTuple, +373 crypto: Box<dyn crypto::Session>, +374 cid_gen: &dyn ConnectionIdGenerator, +375 now: Instant, +376 version: u32, +377 allow_mtud: bool, +378 rng_seed: [u8; 32], +379 side_args: SideArgs, +380 qlog: QlogSink, +381 ) -> Self { +382 let pref_addr_cid = side_args.pref_addr_cid(); +383 let path_validated = side_args.path_validated(); +384 let connection_side = ConnectionSide::from(side_args); +385 let side = connection_side.side(); +386 let mut rng = StdRng::from_seed(rng_seed); +387 let initial_space = PacketSpace::new(now, SpaceId::Initial, &mut rng); +388 let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng); +389 #[cfg(test)] +390 let data_space = match config.deterministic_packet_numbers { +391 true => PacketSpace::new_deterministic(now, SpaceId::Data), +392 false => PacketSpace::new(now, SpaceId::Data, &mut rng), +393 }; +394 #[cfg(not(test))] +395 let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng); +396 let state = State::handshake(state::Handshake { +397 remote_cid_set: side.is_server(), +398 expected_token: Bytes::new(), +399 client_hello: None, +400 allow_server_migration: side.is_client(), +401 }); +402 let local_cid_state = FxHashMap::from_iter([( +403 PathId::ZERO, +404 CidState::new( +405 cid_gen.cid_len(), +406 cid_gen.cid_lifetime(), +407 now, +408 if pref_addr_cid.is_some() { 2 } else { 1 }, +409 ), +410 )]); +411 +412 let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config); +413 // TODO(@divma): consider if we want to delay this until the path is validated +414 path.open = true; +415 let mut this = Self { +416 endpoint_config, +417 crypto_state: CryptoState::new(crypto, init_cid, side, &mut rng), +418 handshake_cid: local_cid, +419 remote_handshake_cid: remote_cid, +420 local_cid_state, +421 paths: BTreeMap::from_iter([( +422 PathId::ZERO, +423 PathState { +424 data: path, +425 prev: None, +426 }, +427 )]), +428 path_generation_counter: 0, +429 allow_mtud, +430 state, +431 side: connection_side, +432 peer_params: TransportParameters::default(), +433 original_remote_cid: remote_cid, +434 initial_dst_cid: init_cid, +435 retry_src_cid: None, +436 events: VecDeque::new(), +437 endpoint_events: VecDeque::new(), +438 spin_enabled: config.allow_spin && rng.random_ratio(7, 8), +439 spin: false, +440 spaces: [initial_space, handshake_space, data_space], +441 highest_space: SpaceKind::Initial, +442 permit_idle_reset: true, +443 idle_timeout: match config.max_idle_timeout { +444 None | Some(VarInt(0)) => None, +445 Some(dur) => Some(Duration::from_millis(dur.0)), +446 }, +447 timers: TimerTable::default(), +448 authentication_failures: 0, +449 connection_close_pending: false, +450 +451 ack_frequency: AckFrequencyState::new(get_max_ack_delay( +452 &TransportParameters::default(), +453 )), +454 +455 app_limited: false, +456 receiving_ecn: false, +457 total_authed_packets: 0, +458 +459 next_observed_addr_seq_no: 0u32.into(), +460 +461 streams: StreamsState::new( +462 side, +463 config.max_concurrent_uni_streams, +464 config.max_concurrent_bidi_streams, +465 config.send_window, +466 config.receive_window, +467 config.stream_receive_window, +468 ), +469 datagrams: DatagramState::default(), +470 config, +471 remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]), +472 rng, +473 stats: ConnectionStats::default(), +474 path_stats: Default::default(), +475 version, +476 +477 // peer params are not yet known, so multipath is not enabled +478 max_concurrent_paths: NonZeroU32::MIN, +479 local_max_path_id: PathId::ZERO, +480 remote_max_path_id: PathId::ZERO, +481 max_path_id_with_cids: PathId::ZERO, +482 abandoned_paths: Default::default(), +483 +484 // iroh's nat traversal +485 iroh_hp: Default::default(), +486 qlog, +487 }; +488 if path_validated { +489 this.on_path_validated(PathId::ZERO); +490 } +491 if side.is_client() { +492 // Kick off the connection +493 this.write_crypto(); +494 this.init_0rtt(now); +495 } +496 this.qlog +497 .emit_tuple_assigned(PathId::ZERO, network_path, now); +498 this +499 } +500 +501 /// Returns the next time at which `handle_timeout` should be called +502 /// +503 /// The value returned may change after: +504 /// - the application performed some I/O on the connection +505 /// - a call was made to `handle_event` +506 /// - a call to `poll_transmit` returned `Some` +507 /// - a call was made to `handle_timeout` +508 #[must_use] +509 pub fn poll_timeout(&mut self) -> Option<Instant> { +510 self.timers.peek() +511 } +512 +513 /// Returns application-facing events +514 /// +515 /// Connections should be polled for events after: +516 /// - a call was made to `handle_event` +517 /// - a call was made to `handle_timeout` +518 #[must_use] +519 pub fn poll(&mut self) -> Option<Event> { +520 if let Some(x) = self.events.pop_front() { +521 return Some(x); +522 } +523 +524 if let Some(event) = self.streams.poll() { +525 return Some(Event::Stream(event)); +526 } +527 +528 if let Some(reason) = self.state.take_error() { +529 return Some(Event::ConnectionLost { reason }); +530 } +531 +532 None +533 } +534 +535 /// Return endpoint-facing events +536 #[must_use] +537 pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> { +538 self.endpoint_events.pop_front().map(EndpointEvent) +539 } +540 +541 /// Provide control over streams +542 #[must_use] +543 pub fn streams(&mut self) -> Streams<'_> { +544 Streams { +545 state: &mut self.streams, +546 conn_state: &self.state, +547 } +548 } +549 +550 /// Provide control over streams +551 #[must_use] +552 pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> { +553 assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side()); +554 RecvStream { +555 id, +556 state: &mut self.streams, +557 pending: &mut self.spaces[SpaceId::Data].pending, +558 } +559 } +560 +561 /// Provide control over streams +562 #[must_use] +563 pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> { +564 assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side()); +565 SendStream { +566 id, +567 state: &mut self.streams, +568 pending: &mut self.spaces[SpaceId::Data].pending, +569 conn_state: &self.state, +570 } +571 } +572 +573 /// Opens a new path only if no path on the same network path currently exists. +574 /// +575 /// This comparison will use [`FourTuple::is_probably_same_path`] on the given `network_path` +576 /// and pass it existing path's network paths. +577 /// +578 /// This means that you can pass `local_ip: None` to make the comparison only compare +579 /// remote addresses. +580 /// +581 /// This avoids having to guess which local interface will be used to communicate with the +582 /// remote, should it not be known yet. We assume that if we already have a path to the remote, +583 /// the OS is likely to use the same interface to talk to said remote. +584 /// +585 /// See also [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id, +586 /// false)` if was opened. +587 /// +588 /// [`open_path`]: Connection::open_path +589 pub fn open_path_ensure( +590 &mut self, +591 network_path: FourTuple, +592 initial_status: PathStatus, +593 now: Instant, +594 ) -> Result<(PathId, bool), PathError> { +595 let existing_open_path = self.paths.iter().find(|(id, path)| { +596 network_path.is_probably_same_path(&path.data.network_path) +597 && !self.abandoned_paths.contains(*id) +598 }); +599 match existing_open_path { +600 Some((path_id, _state)) => Ok((*path_id, true)), +601 None => Ok((self.open_path(network_path, initial_status, now)?, false)), +602 } +603 } +604 +605 /// Opens a new path +606 /// +607 /// Further errors might occur and they will be emitted in [`PathEvent::LocallyClosed`] events. +608 /// When the path is opened it will be reported as an [`PathEvent::Opened`]. +609 pub fn open_path( +610 &mut self, +611 network_path: FourTuple, +612 initial_status: PathStatus, +613 now: Instant, +614 ) -> Result<PathId, PathError> { +615 if !self.is_multipath_negotiated() { +616 return Err(PathError::MultipathNotNegotiated); +617 } +618 if self.side().is_server() { +619 return Err(PathError::ServerSideNotAllowed); +620 } +621 +622 let max_abandoned = self.abandoned_paths.iter().max().copied(); +623 let max_used = self.paths.keys().last().copied(); +624 let path_id = max_abandoned +625 .max(max_used) +626 .unwrap_or(PathId::ZERO) +627 .saturating_add(1u8); +628 +629 if Some(path_id) > self.max_path_id() { +630 return Err(PathError::MaxPathIdReached); +631 } +632 if path_id > self.remote_max_path_id { +633 self.spaces[SpaceId::Data].pending.paths_blocked = true; +634 return Err(PathError::MaxPathIdReached); +635 } +636 if self +637 .remote_cids +638 .get(&path_id) +639 .map(CidQueue::active) +640 .is_none() +641 { +642 self.spaces[SpaceId::Data] +643 .pending +644 .path_cids_blocked +645 .insert(path_id); +646 return Err(PathError::RemoteCidsExhausted); +647 } +648 +649 let path = self.ensure_path(path_id, network_path, now, None); +650 path.status.local_update(initial_status); +651 +652 Ok(path_id) +653 } +654 +655 /// Closes a path by sending a PATH_ABANDON frame +656 /// +657 /// This will not allow closing the last path. It does allow closing paths which have +658 /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer +659 /// for a path that was never opened locally. +660 pub fn close_path( +661 &mut self, +662 now: Instant, +663 path_id: PathId, +664 error_code: VarInt, +665 ) -> Result<(), ClosePathError> { +666 let locally_initiated = true; +667 self.close_path_inner(now, path_id, error_code, locally_initiated) +668 } +669 +670 fn close_path_inner( +671 &mut self, +672 now: Instant, +673 path_id: PathId, +674 error_code: VarInt, +675 locally_initiated: bool, +676 ) -> Result<(), ClosePathError> { +677 if !self.is_multipath_negotiated() { +678 return Err(ClosePathError::MultipathNotNegotiated); +679 } +680 if self.abandoned_paths.contains(&path_id) +681 || Some(path_id) > self.max_path_id() +682 || !self.paths.contains_key(&path_id) +683 { +684 return Err(ClosePathError::ClosedPath); +685 } +686 +687 if locally_initiated { +688 let has_remaining_validated_paths = self.paths.iter().any(|(id, path)| { +689 *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated +690 }); +691 if !has_remaining_validated_paths { +692 return Err(ClosePathError::LastOpenPath); +693 } +694 } else { +695 // The remote abandoned this path. We should always "accept" this. Doing so right now, +696 // however, breaks assumptions throughout the code. We error instead, for the +697 // connection to be killed. See <https://github.com/n0-computer/quinn/issues/397> +698 let has_remaining_paths = self +699 .paths +700 .keys() +701 .any(|id| *id != path_id && !self.abandoned_paths.contains(id)); +702 if !has_remaining_paths { +703 return Err(ClosePathError::LastOpenPath); +704 } +705 } +706 +707 // Send PATH_ABANDON +708 self.spaces[SpaceId::Data] +709 .pending +710 .path_abandon +711 .insert(path_id, error_code.into()); +712 +713 // Remove pending NEW CIDs for this path +714 let pending_space = &mut self.spaces[SpaceId::Data].pending; +715 pending_space.new_cids.retain(|cid| cid.path_id != path_id); +716 pending_space.path_cids_blocked.retain(|&id| id != path_id); +717 pending_space.path_status.retain(|&id| id != path_id); +718 +719 // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths) +720 for space in self.spaces[SpaceId::Data].iter_paths_mut() { +721 for sent_packet in space.sent_packets.values_mut() { +722 if let Some(retransmits) = sent_packet.retransmits.get_mut() { +723 retransmits.new_cids.retain(|cid| cid.path_id != path_id); +724 retransmits.path_cids_blocked.retain(|&id| id != path_id); +725 retransmits.path_status.retain(|&id| id != path_id); +726 } +727 } +728 } +729 +730 // Consider remotely issued CIDs as retired. +731 // Technically we don't have to do this just yet. We only need to do this *after* +732 // the ABANDON_PATH frame is sent, allowing us to still send it on the +733 // to-be-abandoned path. However it is recommended to send it on another path, and +734 // we do not allow abandoning the last path anyway. +735 self.remote_cids.remove(&path_id); +736 self.endpoint_events +737 .push_back(EndpointEventInner::RetireResetToken(path_id)); +738 +739 trace!(%path_id, "abandoning path"); +740 self.abandoned_paths.insert(path_id); +741 +742 // Clear all timers. +743 // We still need some timers after we close a path, e.g. the `DiscardPath` timer, +744 // but that timer is going to be set once we the `PATH_ABANDON` frame is sent. +745 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); +746 +747 Ok(()) +748 } 749 -750 Ok(()) -751 } -752 -753 /// Gets the [`PathData`] for a known [`PathId`]. -754 /// -755 /// Will panic if the path_id does not reference any known path. -756 #[track_caller] -757 fn path_data(&self, path_id: PathId) -> &PathData { -758 if let Some(data) = self.paths.get(&path_id) { -759 &data.data -760 } else { -761 panic!( -762 "unknown path: {path_id}, currently known paths: {:?}", -763 self.paths.keys().collect::<Vec<_>>() -764 ); -765 } -766 } -767 -768 /// Gets a reference to the [`PathData`] for a [`PathId`] -769 fn path(&self, path_id: PathId) -> Option<&PathData> { -770 self.paths.get(&path_id).map(|path_state| &path_state.data) -771 } -772 -773 /// Gets a mutable reference to the [`PathData`] for a [`PathId`] -774 fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> { -775 self.paths -776 .get_mut(&path_id) -777 .map(|path_state| &mut path_state.data) -778 } -779 -780 /// Returns all known paths. -781 /// -782 /// There is no guarantee any of these paths are open or usable. -783 pub fn paths(&self) -> Vec<PathId> { -784 self.paths.keys().copied().collect() -785 } -786 -787 /// Gets the local [`PathStatus`] for a known [`PathId`] -788 pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> { -789 self.path(path_id) -790 .map(PathData::local_status) -791 .ok_or(ClosedPath { _private: () }) -792 } -793 -794 /// Returns the path's network path represented as a 4-tuple. -795 pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> { -796 self.path(path_id) -797 .map(|path| path.network_path) -798 .ok_or(ClosedPath { _private: () }) -799 } -800 -801 /// Sets the [`PathStatus`] for a known [`PathId`] -802 /// -803 /// Returns the previous path status on success. -804 pub fn set_path_status( -805 &mut self, -806 path_id: PathId, -807 status: PathStatus, -808 ) -> Result<PathStatus, SetPathStatusError> { -809 if !self.is_multipath_negotiated() { -810 return Err(SetPathStatusError::MultipathNotNegotiated); -811 } -812 let path = self -813 .path_mut(path_id) -814 .ok_or(SetPathStatusError::ClosedPath)?; -815 let prev = match path.status.local_update(status) { -816 Some(prev) => { -817 self.spaces[SpaceId::Data] -818 .pending -819 .path_status -820 .insert(path_id); -821 prev -822 } -823 None => path.local_status(), -824 }; -825 Ok(prev) -826 } -827 -828 /// Returns the remote path status -829 // TODO(flub): Probably should also be some kind of path event? Not even sure if I like -830 // this as an API, but for now it allows me to write a test easily. -831 // TODO(flub): Technically this should be a Result<Option<PathSTatus>>? -832 pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> { -833 self.path(path_id).and_then(|path| path.remote_status()) -834 } -835 -836 /// Sets the max_idle_timeout for a specific path -837 /// -838 /// See [`TransportConfig::default_path_max_idle_timeout`] for details. -839 /// -840 /// Returns the previous value of the setting. -841 pub fn set_path_max_idle_timeout( -842 &mut self, -843 path_id: PathId, -844 timeout: Option<Duration>, -845 ) -> Result<Option<Duration>, ClosedPath> { -846 let path = self -847 .paths -848 .get_mut(&path_id) -849 .ok_or(ClosedPath { _private: () })?; -850 Ok(std::mem::replace(&mut path.data.idle_timeout, timeout)) -851 } -852 -853 /// Sets the keep_alive_interval for a specific path -854 /// -855 /// See [`TransportConfig::default_path_keep_alive_interval`] for details. -856 /// -857 /// Returns the previous value of the setting. -858 pub fn set_path_keep_alive_interval( -859 &mut self, -860 path_id: PathId, -861 interval: Option<Duration>, -862 ) -> Result<Option<Duration>, ClosedPath> { -863 let path = self -864 .paths -865 .get_mut(&path_id) -866 .ok_or(ClosedPath { _private: () })?; -867 Ok(std::mem::replace(&mut path.data.keep_alive, interval)) -868 } -869 -870 /// Gets the [`PathData`] for a known [`PathId`]. -871 /// -872 /// Will panic if the path_id does not reference any known path. -873 #[track_caller] -874 fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData { -875 &mut self.paths.get_mut(&path_id).expect("known path").data -876 } -877 -878 /// Find an open, validated path that's on the same network path as the given network path. -879 /// -880 /// Returns the first path matching, even if there's multiple. -881 fn find_validated_path_on_network_path( -882 &self, -883 network_path: FourTuple, -884 ) -> Option<(&PathId, &PathState)> { -885 self.paths.iter().find(|(path_id, path_state)| { -886 path_state.data.validated -887 // Would this use the same network path, if network_path were used to send right now? -888 && network_path.is_probably_same_path(&path_state.data.network_path) -889 && !self.abandoned_paths.contains(path_id) -890 }) -891 // TODO(@divma): we might want to ensure the path has been recently active to consider the -892 // address validated -893 // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives? -894 } -895 -896 /// Creates the [`PathData`] for a new [`PathId`]. -897 /// -898 /// Called for incoming packets as well as when opening a new path locally. -899 fn ensure_path( -900 &mut self, -901 path_id: PathId, -902 network_path: FourTuple, -903 now: Instant, -904 pn: Option<u64>, -905 ) -> &mut PathData { -906 let valid_path = self.find_validated_path_on_network_path(network_path); -907 let validated = valid_path.is_some(); -908 let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative()); -909 let vacant_entry = match self.paths.entry(path_id) { -910 btree_map::Entry::Vacant(vacant_entry) => vacant_entry, -911 btree_map::Entry::Occupied(occupied_entry) => { -912 return &mut occupied_entry.into_mut().data; -913 } -914 }; -915 -916 debug!(%validated, %path_id, %network_path, "path added"); -917 let peer_max_udp_payload_size = -918 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); -919 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); -920 let mut data = PathData::new( -921 network_path, -922 self.allow_mtud, -923 Some(peer_max_udp_payload_size), -924 self.path_generation_counter, -925 now, -926 &self.config, -927 ); -928 -929 data.validated = validated; -930 if let Some(initial_rtt) = initial_rtt { -931 data.rtt.reset_initial_rtt(initial_rtt); -932 } -933 -934 let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base(); -935 self.timers.set( -936 Timer::PerPath(path_id, PathTimer::PathOpen), -937 now + 3 * pto, -938 self.qlog.with_time(now), -939 ); -940 -941 // To open a path locally we need to send a packet on the path. Sending a challenge -942 // guarantees this. -943 data.send_new_challenge = true; -944 -945 let path = vacant_entry.insert(PathState { data, prev: None }); -946 -947 let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng); -948 if let Some(pn) = pn { -949 pn_space.dedup.insert(pn); -950 } -951 self.spaces[SpaceId::Data] -952 .number_spaces -953 .insert(path_id, pn_space); -954 self.qlog.emit_tuple_assigned(path_id, network_path, now); -955 -956 // If the remote opened this path we may not have CIDs for it. For locally opened -957 // paths the caller should have already made sure we have CIDs and refused to open -958 // it if there were none. -959 if !self.remote_cids.contains_key(&path_id) { -960 debug!(%path_id, "Remote opened path without issuing CIDs"); -961 self.spaces[SpaceId::Data] -962 .pending -963 .path_cids_blocked -964 .insert(path_id); -965 // Do not abandon this path right away. CIDs might be in-flight still and arrive -966 // soon. It is up to the remote to handle this situation. -967 } +750 /// Gets the [`PathData`] for a known [`PathId`]. +751 /// +752 /// Will panic if the path_id does not reference any known path. +753 #[track_caller] +754 fn path_data(&self, path_id: PathId) -> &PathData { +755 if let Some(data) = self.paths.get(&path_id) { +756 &data.data +757 } else { +758 panic!( +759 "unknown path: {path_id}, currently known paths: {:?}", +760 self.paths.keys().collect::<Vec<_>>() +761 ); +762 } +763 } +764 +765 /// Gets a reference to the [`PathData`] for a [`PathId`] +766 fn path(&self, path_id: PathId) -> Option<&PathData> { +767 self.paths.get(&path_id).map(|path_state| &path_state.data) +768 } +769 +770 /// Gets a mutable reference to the [`PathData`] for a [`PathId`] +771 fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> { +772 self.paths +773 .get_mut(&path_id) +774 .map(|path_state| &mut path_state.data) +775 } +776 +777 /// Returns all known paths. +778 /// +779 /// There is no guarantee any of these paths are open or usable. +780 pub fn paths(&self) -> Vec<PathId> { +781 self.paths.keys().copied().collect() +782 } +783 +784 /// Gets the local [`PathStatus`] for a known [`PathId`] +785 pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> { +786 self.path(path_id) +787 .map(PathData::local_status) +788 .ok_or(ClosedPath { _private: () }) +789 } +790 +791 /// Returns the path's network path represented as a 4-tuple. +792 pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> { +793 self.path(path_id) +794 .map(|path| path.network_path) +795 .ok_or(ClosedPath { _private: () }) +796 } +797 +798 /// Sets the [`PathStatus`] for a known [`PathId`] +799 /// +800 /// Returns the previous path status on success. +801 pub fn set_path_status( +802 &mut self, +803 path_id: PathId, +804 status: PathStatus, +805 ) -> Result<PathStatus, SetPathStatusError> { +806 if !self.is_multipath_negotiated() { +807 return Err(SetPathStatusError::MultipathNotNegotiated); +808 } +809 let path = self +810 .path_mut(path_id) +811 .ok_or(SetPathStatusError::ClosedPath)?; +812 let prev = match path.status.local_update(status) { +813 Some(prev) => { +814 self.spaces[SpaceId::Data] +815 .pending +816 .path_status +817 .insert(path_id); +818 prev +819 } +820 None => path.local_status(), +821 }; +822 Ok(prev) +823 } +824 +825 /// Returns the remote path status +826 // TODO(flub): Probably should also be some kind of path event? Not even sure if I like +827 // this as an API, but for now it allows me to write a test easily. +828 // TODO(flub): Technically this should be a Result<Option<PathSTatus>>? +829 pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> { +830 self.path(path_id).and_then(|path| path.remote_status()) +831 } +832 +833 /// Sets the max_idle_timeout for a specific path +834 /// +835 /// See [`TransportConfig::default_path_max_idle_timeout`] for details. +836 /// +837 /// Returns the previous value of the setting. +838 pub fn set_path_max_idle_timeout( +839 &mut self, +840 path_id: PathId, +841 timeout: Option<Duration>, +842 ) -> Result<Option<Duration>, ClosedPath> { +843 let path = self +844 .paths +845 .get_mut(&path_id) +846 .ok_or(ClosedPath { _private: () })?; +847 Ok(std::mem::replace(&mut path.data.idle_timeout, timeout)) +848 } +849 +850 /// Sets the keep_alive_interval for a specific path +851 /// +852 /// See [`TransportConfig::default_path_keep_alive_interval`] for details. +853 /// +854 /// Returns the previous value of the setting. +855 pub fn set_path_keep_alive_interval( +856 &mut self, +857 path_id: PathId, +858 interval: Option<Duration>, +859 ) -> Result<Option<Duration>, ClosedPath> { +860 let path = self +861 .paths +862 .get_mut(&path_id) +863 .ok_or(ClosedPath { _private: () })?; +864 Ok(std::mem::replace(&mut path.data.keep_alive, interval)) +865 } +866 +867 /// Gets the [`PathData`] for a known [`PathId`]. +868 /// +869 /// Will panic if the path_id does not reference any known path. +870 #[track_caller] +871 fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData { +872 &mut self.paths.get_mut(&path_id).expect("known path").data +873 } +874 +875 /// Find an open, validated path that's on the same network path as the given network path. +876 /// +877 /// Returns the first path matching, even if there's multiple. +878 fn find_validated_path_on_network_path( +879 &self, +880 network_path: FourTuple, +881 ) -> Option<(&PathId, &PathState)> { +882 self.paths.iter().find(|(path_id, path_state)| { +883 path_state.data.validated +884 // Would this use the same network path, if network_path were used to send right now? +885 && network_path.is_probably_same_path(&path_state.data.network_path) +886 && !self.abandoned_paths.contains(path_id) +887 }) +888 // TODO(@divma): we might want to ensure the path has been recently active to consider the +889 // address validated +890 // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives? +891 } +892 +893 /// Creates the [`PathData`] for a new [`PathId`]. +894 /// +895 /// Called for incoming packets as well as when opening a new path locally. +896 fn ensure_path( +897 &mut self, +898 path_id: PathId, +899 network_path: FourTuple, +900 now: Instant, +901 pn: Option<u64>, +902 ) -> &mut PathData { +903 let valid_path = self.find_validated_path_on_network_path(network_path); +904 let validated = valid_path.is_some(); +905 let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative()); +906 let vacant_entry = match self.paths.entry(path_id) { +907 btree_map::Entry::Vacant(vacant_entry) => vacant_entry, +908 btree_map::Entry::Occupied(occupied_entry) => { +909 return &mut occupied_entry.into_mut().data; +910 } +911 }; +912 +913 debug!(%validated, %path_id, %network_path, "path added"); +914 let peer_max_udp_payload_size = +915 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); +916 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); +917 let mut data = PathData::new( +918 network_path, +919 self.allow_mtud, +920 Some(peer_max_udp_payload_size), +921 self.path_generation_counter, +922 now, +923 &self.config, +924 ); +925 +926 data.validated = validated; +927 if let Some(initial_rtt) = initial_rtt { +928 data.rtt.reset_initial_rtt(initial_rtt); +929 } +930 +931 let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base(); +932 self.timers.set( +933 Timer::PerPath(path_id, PathTimer::PathOpen), +934 now + 3 * pto, +935 self.qlog.with_time(now), +936 ); +937 +938 // To open a path locally we need to send a packet on the path. Sending a challenge +939 // guarantees this. +940 data.send_new_challenge = true; +941 +942 let path = vacant_entry.insert(PathState { data, prev: None }); +943 +944 let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng); +945 if let Some(pn) = pn { +946 pn_space.dedup.insert(pn); +947 } +948 self.spaces[SpaceId::Data] +949 .number_spaces +950 .insert(path_id, pn_space); +951 self.qlog.emit_tuple_assigned(path_id, network_path, now); +952 +953 // If the remote opened this path we may not have CIDs for it. For locally opened +954 // paths the caller should have already made sure we have CIDs and refused to open +955 // it if there were none. +956 if !self.remote_cids.contains_key(&path_id) { +957 debug!(%path_id, "Remote opened path without issuing CIDs"); +958 self.spaces[SpaceId::Data] +959 .pending +960 .path_cids_blocked +961 .insert(path_id); +962 // Do not abandon this path right away. CIDs might be in-flight still and arrive +963 // soon. It is up to the remote to handle this situation. +964 } +965 +966 &mut path.data +967 } 968 -969 &mut path.data -970 } -971 -972 /// Returns packets to transmit -973 /// -974 /// Connections should be polled for transmit after: -975 /// - the application performed some I/O on the connection -976 /// - a call was made to `handle_event` -977 /// - a call was made to `handle_timeout` -978 /// -979 /// `max_datagrams` specifies how many datagrams can be returned inside a -980 /// single Transmit using GSO. This must be at least 1. -981 #[must_use] -982 pub fn poll_transmit( -983 &mut self, -984 now: Instant, -985 max_datagrams: NonZeroUsize, -986 buf: &mut Vec<u8>, -987 ) -> Option<Transmit> { -988 let max_datagrams = match self.config.enable_segmentation_offload { -989 false => NonZeroUsize::MIN, -990 true => max_datagrams, -991 }; -992 -993 // Each call to poll_transmit can only send datagrams to one destination, because -994 // all datagrams in a GSO batch are for the same destination. Therefore only -995 // datagrams for one destination address are produced for each poll_transmit call. -996 -997 // Check whether we need to send a close message -998 let connection_close_pending = match self.state.as_type() { -999 StateType::Drained => { -1000 self.app_limited = true; -1001 return None; -1002 } -1003 StateType::Draining | StateType::Closed => { -1004 // self.close is only reset once the associated packet had been -1005 // encoded successfully -1006 if !self.connection_close_pending { -1007 self.app_limited = true; -1008 return None; -1009 } -1010 true -1011 } -1012 _ => false, -1013 }; -1014 -1015 // Schedule an ACK_FREQUENCY frame if a new one needs to be sent. -1016 if let Some(config) = &self.config.ack_frequency_config { -1017 let rtt = self -1018 .paths -1019 .values() -1020 .map(|p| p.data.rtt.get()) -1021 .min() -1022 .expect("one path exists"); -1023 self.spaces[SpaceId::Data].pending.ack_frequency = self -1024 .ack_frequency -1025 .should_send_ack_frequency(rtt, config, &self.peer_params) -1026 && self.highest_space == SpaceKind::Data -1027 && self.peer_supports_ack_frequency(); -1028 } +969 /// Returns packets to transmit +970 /// +971 /// Connections should be polled for transmit after: +972 /// - the application performed some I/O on the connection +973 /// - a call was made to `handle_event` +974 /// - a call was made to `handle_timeout` +975 /// +976 /// `max_datagrams` specifies how many datagrams can be returned inside a +977 /// single Transmit using GSO. This must be at least 1. +978 #[must_use] +979 pub fn poll_transmit( +980 &mut self, +981 now: Instant, +982 max_datagrams: NonZeroUsize, +983 buf: &mut Vec<u8>, +984 ) -> Option<Transmit> { +985 let max_datagrams = match self.config.enable_segmentation_offload { +986 false => NonZeroUsize::MIN, +987 true => max_datagrams, +988 }; +989 +990 // Each call to poll_transmit can only send datagrams to one destination, because +991 // all datagrams in a GSO batch are for the same destination. Therefore only +992 // datagrams for one destination address are produced for each poll_transmit call. +993 +994 // Check whether we need to send a close message +995 let connection_close_pending = match self.state.as_type() { +996 StateType::Drained => { +997 self.app_limited = true; +998 return None; +999 } +1000 StateType::Draining | StateType::Closed => { +1001 // self.close is only reset once the associated packet had been +1002 // encoded successfully +1003 if !self.connection_close_pending { +1004 self.app_limited = true; +1005 return None; +1006 } +1007 true +1008 } +1009 _ => false, +1010 }; +1011 +1012 // Schedule an ACK_FREQUENCY frame if a new one needs to be sent. +1013 if let Some(config) = &self.config.ack_frequency_config { +1014 let rtt = self +1015 .paths +1016 .values() +1017 .map(|p| p.data.rtt.get()) +1018 .min() +1019 .expect("one path exists"); +1020 self.spaces[SpaceId::Data].pending.ack_frequency = self +1021 .ack_frequency +1022 .should_send_ack_frequency(rtt, config, &self.peer_params) +1023 && self.highest_space == SpaceKind::Data +1024 && self.peer_supports_ack_frequency(); +1025 } +1026 +1027 // TODO(flub): path scheduling logic might be buggy if there are only un-validated +1028 // paths and PATH_STATUS_BACKUP paths. 1029 -1030 // TODO(flub): path scheduling logic might be buggy if there are only un-validated -1031 // paths and PATH_STATUS_BACKUP paths. -1032 -1033 // Path scheduling logic is currently as such: +1030 // Path scheduling logic is currently as such: +1031 // +1032 // - For any un-validated paths we only send frames that *must* be sent on that +1033 // path. E.g. PATH_CHALLENGE, PATH_RESPONSE. 1034 // -1035 // - For any un-validated paths we only send frames that *must* be sent on that -1036 // path. E.g. PATH_CHALLENGE, PATH_RESPONSE. -1037 // -1038 // - If there are any validated paths with CIDs and PathStatus::Available: -1039 // - Frames that can be sent on any path, e.g. STREAM, DATAGRAM, are only sent on -1040 // these available paths. -1041 // - All other paths only send frames that *must* be sent on those paths, -1042 // e.g. PATH_CHALLENGE, PATH_RESPONSE, tail-loss probes, keep alive PING. +1035 // - If there are any validated paths with CIDs and PathStatus::Available: +1036 // - Frames that can be sent on any path, e.g. STREAM, DATAGRAM, are only sent on +1037 // these available paths. +1038 // - All other paths only send frames that *must* be sent on those paths, +1039 // e.g. PATH_CHALLENGE, PATH_RESPONSE, tail-loss probes, keep alive PING. +1040 // +1041 // - If there are no validated paths with CIDs and PathStatus::Available all frames +1042 // are sent on the earlierst possible path. 1043 // -1044 // - If there are no validated paths with CIDs and PathStatus::Available all frames -1045 // are sent on the earlierst possible path. -1046 // -1047 // For all this we use the *path_exclusive_only* boolean: If set to true, only -1048 // frames that must be sent on the path will be built into the packet. -1049 -1050 // Is there any open, validated and status available path with dst CIDs? If so we'll -1051 // want to set path_exclusive_only for any other paths. -1052 let have_available_path = self.paths.iter().any(|(id, path)| { -1053 path.data.validated -1054 && path.data.local_status() == PathStatus::Available -1055 && self.remote_cids.contains_key(id) -1056 }); -1057 -1058 // TODO: how to avoid the allocation? Cannot use a for loop because of -1059 // borrowing. Maybe SmallVec or similar. -1060 let path_ids: Vec<_> = self.paths.keys().copied().collect(); -1061 -1062 // If we end up not sending anything, we need to know if that was because there was -1063 // nothing to send or because we were congestion blocked. -1064 let mut congestion_blocked = false; -1065 -1066 for &path_id in &path_ids { -1067 if !connection_close_pending -1068 && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id) -1069 { -1070 return Some(transmit); -1071 } -1072 -1073 // Poll for on-path transmits. -1074 match self.poll_transmit_on_path( -1075 now, -1076 buf, -1077 path_id, -1078 max_datagrams, -1079 have_available_path, -1080 connection_close_pending, -1081 ) { -1082 PollPathStatus::Send(transmit) => { -1083 return Some(transmit); -1084 } -1085 PollPathStatus::NothingToSend { -1086 congestion_blocked: cb, -1087 } => { -1088 congestion_blocked |= cb; -1089 // Continue checking other paths, tail-loss probes may need to be sent -1090 // in all spaces. -1091 debug_assert!( -1092 buf.is_empty(), -1093 "nothing to send on path but buffer not empty" -1094 ); -1095 } -1096 } -1097 } -1098 -1099 // We didn't produce any application data packet -1100 debug_assert!( -1101 buf.is_empty(), -1102 "there was data in the buffer, but it was not sent" -1103 ); -1104 -1105 self.app_limited = !congestion_blocked; -1106 -1107 if self.state.is_established() { -1108 // Try MTU probing now -1109 for path_id in path_ids { -1110 if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) { -1111 return Some(transmit); -1112 } -1113 } -1114 } +1044 // For all this we use the *path_exclusive_only* boolean: If set to true, only +1045 // frames that must be sent on the path will be built into the packet. +1046 +1047 // Is there any open, validated and status available path with dst CIDs? If so we'll +1048 // want to set path_exclusive_only for any other paths. +1049 let have_available_path = self.paths.iter().any(|(id, path)| { +1050 path.data.validated +1051 && path.data.local_status() == PathStatus::Available +1052 && self.remote_cids.contains_key(id) +1053 }); +1054 +1055 // TODO: how to avoid the allocation? Cannot use a for loop because of +1056 // borrowing. Maybe SmallVec or similar. +1057 let path_ids: Vec<_> = self.paths.keys().copied().collect(); +1058 +1059 // If we end up not sending anything, we need to know if that was because there was +1060 // nothing to send or because we were congestion blocked. +1061 let mut congestion_blocked = false; +1062 +1063 for &path_id in &path_ids { +1064 if !connection_close_pending +1065 && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id) +1066 { +1067 return Some(transmit); +1068 } +1069 +1070 // Poll for on-path transmits. +1071 match self.poll_transmit_on_path( +1072 now, +1073 buf, +1074 path_id, +1075 max_datagrams, +1076 have_available_path, +1077 connection_close_pending, +1078 ) { +1079 PollPathStatus::Send(transmit) => { +1080 return Some(transmit); +1081 } +1082 PollPathStatus::NothingToSend { +1083 congestion_blocked: cb, +1084 } => { +1085 congestion_blocked |= cb; +1086 // Continue checking other paths, tail-loss probes may need to be sent +1087 // in all spaces. +1088 debug_assert!( +1089 buf.is_empty(), +1090 "nothing to send on path but buffer not empty" +1091 ); +1092 } +1093 } +1094 } +1095 +1096 // We didn't produce any application data packet +1097 debug_assert!( +1098 buf.is_empty(), +1099 "there was data in the buffer, but it was not sent" +1100 ); +1101 +1102 self.app_limited = !congestion_blocked; +1103 +1104 if self.state.is_established() { +1105 // Try MTU probing now +1106 for path_id in path_ids { +1107 if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) { +1108 return Some(transmit); +1109 } +1110 } +1111 } +1112 +1113 None +1114 } 1115 -1116 None -1117 } -1118 -1119 fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit { -1120 debug_assert!( -1121 !transmit.is_empty(), -1122 "must not be called with an empty transmit buffer" -1123 ); -1124 -1125 let network_path = self.path_data(path_id).network_path; -1126 trace!( -1127 segment_size = transmit.segment_size(), -1128 last_datagram_len = transmit.len() % transmit.segment_size(), -1129 %network_path, -1130 "sending {} bytes in {} datagrams", -1131 transmit.len(), -1132 transmit.num_datagrams() -1133 ); -1134 self.path_data_mut(path_id) -1135 .inc_total_sent(transmit.len() as u64); -1136 -1137 self.stats -1138 .udp_tx -1139 .on_sent(transmit.num_datagrams() as u64, transmit.len()); -1140 self.path_stats -1141 .entry(path_id) -1142 .or_default() -1143 .udp_tx -1144 .on_sent(transmit.num_datagrams() as u64, transmit.len()); -1145 -1146 Transmit { -1147 destination: network_path.remote, -1148 size: transmit.len(), -1149 ecn: if self.path_data(path_id).sending_ecn { -1150 Some(EcnCodepoint::Ect0) -1151 } else { -1152 None -1153 }, -1154 segment_size: match transmit.num_datagrams() { -1155 1 => None, -1156 _ => Some(transmit.segment_size()), -1157 }, -1158 src_ip: network_path.local_ip, -1159 } -1160 } -1161 -1162 /// poll_transmit logic for off-path data. -1163 fn poll_transmit_off_path( -1164 &mut self, -1165 now: Instant, -1166 buf: &mut Vec<u8>, -1167 path_id: PathId, -1168 ) -> Option<Transmit> { -1169 if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) { -1170 return Some(challenge); +1116 fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit { +1117 debug_assert!( +1118 !transmit.is_empty(), +1119 "must not be called with an empty transmit buffer" +1120 ); +1121 +1122 let network_path = self.path_data(path_id).network_path; +1123 trace!( +1124 segment_size = transmit.segment_size(), +1125 last_datagram_len = transmit.len() % transmit.segment_size(), +1126 %network_path, +1127 "sending {} bytes in {} datagrams", +1128 transmit.len(), +1129 transmit.num_datagrams() +1130 ); +1131 self.path_data_mut(path_id) +1132 .inc_total_sent(transmit.len() as u64); +1133 +1134 self.stats +1135 .udp_tx +1136 .on_sent(transmit.num_datagrams() as u64, transmit.len()); +1137 self.path_stats +1138 .entry(path_id) +1139 .or_default() +1140 .udp_tx +1141 .on_sent(transmit.num_datagrams() as u64, transmit.len()); +1142 +1143 Transmit { +1144 destination: network_path.remote, +1145 size: transmit.len(), +1146 ecn: if self.path_data(path_id).sending_ecn { +1147 Some(EcnCodepoint::Ect0) +1148 } else { +1149 None +1150 }, +1151 segment_size: match transmit.num_datagrams() { +1152 1 => None, +1153 _ => Some(transmit.segment_size()), +1154 }, +1155 src_ip: network_path.local_ip, +1156 } +1157 } +1158 +1159 /// poll_transmit logic for off-path data. +1160 fn poll_transmit_off_path( +1161 &mut self, +1162 now: Instant, +1163 buf: &mut Vec<u8>, +1164 path_id: PathId, +1165 ) -> Option<Transmit> { +1166 if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) { +1167 return Some(challenge); +1168 } +1169 if let Some(response) = self.send_off_path_path_response(now, buf, path_id) { +1170 return Some(response); 1171 } -1172 if let Some(response) = self.send_off_path_path_response(now, buf, path_id) { -1173 return Some(response); +1172 if let Some(challenge) = self.send_nat_traversal_path_challenge(now, buf, path_id) { +1173 return Some(challenge); 1174 } -1175 if let Some(challenge) = self.send_nat_traversal_path_challenge(now, buf, path_id) { -1176 return Some(challenge); -1177 } -1178 None -1179 } -1180 -1181 /// poll_transmit logic for on-path data. +1175 None +1176 } +1177 +1178 /// poll_transmit logic for on-path data. +1179 /// +1180 /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`] +1181 /// has 3 packet spaces, which this handles. 1182 /// -1183 /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`] -1184 /// has 3 packet spaces, which this handles. -1185 /// -1186 /// See [`Self::poll_transmit_off_path`] for off-path data. -1187 #[must_use] -1188 fn poll_transmit_on_path( -1189 &mut self, -1190 now: Instant, -1191 buf: &mut Vec<u8>, -1192 path_id: PathId, -1193 max_datagrams: NonZeroUsize, -1194 have_available_path: bool, -1195 connection_close_pending: bool, -1196 ) -> PollPathStatus { -1197 // Check if there is at least one active CID to use for sending -1198 let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else { -1199 if !self.abandoned_paths.contains(&path_id) { -1200 debug!(%path_id, "no remote CIDs for path"); -1201 } -1202 return PollPathStatus::NothingToSend { -1203 congestion_blocked: false, -1204 }; -1205 }; -1206 -1207 // Whether the last packet in the datagram must be padded so the datagram takes up -1208 // an exact size. An earlier space can decide to not fill an entire datagram and -1209 // require the next space to fill it further. But may need a specific size of the -1210 // datagram containing the packet. The final packet built in the datagram must pad -1211 // to this size. -1212 let mut pad_datagram = PadDatagram::No; -1213 -1214 // The packet number of the last built packet. This is kept kept across spaces. -1215 // QUIC is supposed to have a single congestion controller for the Initial, -1216 // Handshake and Data(PathId::ZERO) spaces. -1217 let mut last_packet_number = None; -1218 -1219 // If we end up not sending anything, we need to know if that was because there was -1220 // nothing to send or because we were congestion blocked. -1221 let mut congestion_blocked = false; -1222 -1223 // Set the segment size to this path's MTU for on-path data. -1224 let pmtu = self.path_data(path_id).current_mtu().into(); -1225 let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu); -1226 -1227 // Iterate over the available spaces. -1228 for space_id in SpaceId::iter() { -1229 // Only PathId::ZERO uses non Data space ids. -1230 if path_id != PathId::ZERO && space_id != SpaceId::Data { -1231 continue; -1232 } -1233 match self.poll_transmit_path_space( -1234 now, -1235 &mut transmit, -1236 path_id, -1237 space_id, -1238 remote_cid, -1239 have_available_path, -1240 connection_close_pending, -1241 pad_datagram, -1242 ) { -1243 PollPathSpaceStatus::NothingToSend { -1244 congestion_blocked: cb, -1245 } => { -1246 congestion_blocked |= cb; -1247 // Continue checking other spaces, tail-loss probes may need to be sent -1248 // in all spaces. -1249 } -1250 PollPathSpaceStatus::WrotePacket { -1251 last_packet_number: pn, -1252 pad_datagram: pad, -1253 } => { -1254 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); -1255 last_packet_number = Some(pn); -1256 pad_datagram = pad; -1257 // Always check higher spaces. If the transmit is full or they have -1258 // nothing to send they will not write packets. But if they can, they -1259 // must always be allowed to add to this transmit because coalescing may -1260 // be required. -1261 continue; -1262 } -1263 PollPathSpaceStatus::Send { -1264 last_packet_number: pn, -1265 } => { -1266 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); -1267 last_packet_number = Some(pn); -1268 break; -1269 } -1270 } -1271 } -1272 -1273 if last_packet_number.is_some() || congestion_blocked { -1274 self.qlog.emit_recovery_metrics( -1275 path_id, -1276 &mut self.paths.get_mut(&path_id).unwrap().data, -1277 now, -1278 ); -1279 } -1280 -1281 match last_packet_number { -1282 Some(last_packet_number) => { -1283 // Note that when sending in multiple spaces the last packet number will be -1284 // the one from the highest space. -1285 self.path_data_mut(path_id).congestion.on_sent( -1286 now, -1287 transmit.len() as u64, -1288 last_packet_number, -1289 ); -1290 PollPathStatus::Send(self.build_transmit(path_id, transmit)) -1291 } -1292 None => PollPathStatus::NothingToSend { congestion_blocked }, -1293 } -1294 } -1295 -1296 /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId). -1297 #[must_use] -1298 fn poll_transmit_path_space( -1299 &mut self, -1300 now: Instant, -1301 transmit: &mut TransmitBuf<'_>, -1302 path_id: PathId, -1303 space_id: SpaceId, -1304 remote_cid: ConnectionId, -1305 // If any other packet space has a usable path with PathStatus::Available. -1306 have_available_path: bool, -1307 // If we need to send a CONNECTION_CLOSE frame. -1308 connection_close_pending: bool, -1309 // Whether the current datagram needs to be padded to a certain size. -1310 mut pad_datagram: PadDatagram, -1311 ) -> PollPathSpaceStatus { -1312 // Keep track of the last packet number we wrote. If None we did not write any -1313 // packets. -1314 let mut last_packet_number = None; -1315 -1316 // Each loop of this may build one packet. It works logically as follows: -1317 // -1318 // - Check if something *needs* to be sent in this space and *can* be sent. -1319 // - If not, return to the caller who will call us again for the next space. -1320 // - Start a new datagram. -1321 // - Unless coalescing the packet into an existing datagram. -1322 // - Write the packet header and payload. -1323 // - Check if coalescing a next packet into the datagram is possible. -1324 // - If coalescing, finish packet without padding to leave space in the datagram. -1325 // - If not coalescing, complete the datagram: -1326 // - Finish packet with padding. -1327 // - Set the transmit segment size if this is the first datagram. -1328 // - Loop: next iteration will exit the loop if nothing more to send in this -1329 // space. The TransmitBuf will contain a started datagram with space if -1330 // coalescing, or completely filled datagram if not coalescing. -1331 loop { -1332 // Determine if anything can be sent in this packet number space (SpaceId + PathId). -1333 let max_packet_size = if transmit.datagram_remaining_mut() > 0 { -1334 // A datagram is started already, we are coalescing another packet into it. -1335 transmit.datagram_remaining_mut() -1336 } else { -1337 // A new datagram needs to be started. -1338 transmit.segment_size() -1339 }; -1340 let can_send = -1341 self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending); -1342 -1343 // Whether we would like to send any frames on this packet space. See the packet -1344 // scheduling described in poll_transmit. -1345 let space_should_send = { -1346 let path_exclusive_only = space_id == SpaceId::Data -1347 && have_available_path -1348 && self.path_data(path_id).local_status() == PathStatus::Backup; -1349 let path_should_send = if path_exclusive_only { -1350 can_send.path_exclusive -1351 } else { -1352 !can_send.is_empty() -1353 }; -1354 let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; -1355 path_should_send || needs_loss_probe -1356 }; -1357 -1358 if !space_should_send { -1359 // Nothing more to send. Previous iterations of this loop may have built -1360 // packets already. -1361 return match last_packet_number { -1362 Some(pn) => PollPathSpaceStatus::WrotePacket { -1363 last_packet_number: pn, -1364 pad_datagram, -1365 }, -1366 None => { -1367 // Only log for spaces which have crypto. -1368 if self.crypto_state.has_keys(space_id.encryption_level()) -1369 || (space_id == SpaceId::Data -1370 && self.crypto_state.has_keys(EncryptionLevel::ZeroRtt)) -1371 { -1372 trace!(?space_id, %path_id, "nothing to send in space"); +1183 /// See [`Self::poll_transmit_off_path`] for off-path data. +1184 #[must_use] +1185 fn poll_transmit_on_path( +1186 &mut self, +1187 now: Instant, +1188 buf: &mut Vec<u8>, +1189 path_id: PathId, +1190 max_datagrams: NonZeroUsize, +1191 have_available_path: bool, +1192 connection_close_pending: bool, +1193 ) -> PollPathStatus { +1194 // Check if there is at least one active CID to use for sending +1195 let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else { +1196 if !self.abandoned_paths.contains(&path_id) { +1197 debug!(%path_id, "no remote CIDs for path"); +1198 } +1199 return PollPathStatus::NothingToSend { +1200 congestion_blocked: false, +1201 }; +1202 }; +1203 +1204 // Whether the last packet in the datagram must be padded so the datagram takes up +1205 // an exact size. An earlier space can decide to not fill an entire datagram and +1206 // require the next space to fill it further. But may need a specific size of the +1207 // datagram containing the packet. The final packet built in the datagram must pad +1208 // to this size. +1209 let mut pad_datagram = PadDatagram::No; +1210 +1211 // The packet number of the last built packet. This is kept kept across spaces. +1212 // QUIC is supposed to have a single congestion controller for the Initial, +1213 // Handshake and Data(PathId::ZERO) spaces. +1214 let mut last_packet_number = None; +1215 +1216 // If we end up not sending anything, we need to know if that was because there was +1217 // nothing to send or because we were congestion blocked. +1218 let mut congestion_blocked = false; +1219 +1220 // Set the segment size to this path's MTU for on-path data. +1221 let pmtu = self.path_data(path_id).current_mtu().into(); +1222 let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu); +1223 +1224 // Iterate over the available spaces. +1225 for space_id in SpaceId::iter() { +1226 // Only PathId::ZERO uses non Data space ids. +1227 if path_id != PathId::ZERO && space_id != SpaceId::Data { +1228 continue; +1229 } +1230 match self.poll_transmit_path_space( +1231 now, +1232 &mut transmit, +1233 path_id, +1234 space_id, +1235 remote_cid, +1236 have_available_path, +1237 connection_close_pending, +1238 pad_datagram, +1239 ) { +1240 PollPathSpaceStatus::NothingToSend { +1241 congestion_blocked: cb, +1242 } => { +1243 congestion_blocked |= cb; +1244 // Continue checking other spaces, tail-loss probes may need to be sent +1245 // in all spaces. +1246 } +1247 PollPathSpaceStatus::WrotePacket { +1248 last_packet_number: pn, +1249 pad_datagram: pad, +1250 } => { +1251 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); +1252 last_packet_number = Some(pn); +1253 pad_datagram = pad; +1254 // Always check higher spaces. If the transmit is full or they have +1255 // nothing to send they will not write packets. But if they can, they +1256 // must always be allowed to add to this transmit because coalescing may +1257 // be required. +1258 continue; +1259 } +1260 PollPathSpaceStatus::Send { +1261 last_packet_number: pn, +1262 } => { +1263 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); +1264 last_packet_number = Some(pn); +1265 break; +1266 } +1267 } +1268 } +1269 +1270 if last_packet_number.is_some() || congestion_blocked { +1271 self.qlog.emit_recovery_metrics( +1272 path_id, +1273 &mut self.paths.get_mut(&path_id).unwrap().data, +1274 now, +1275 ); +1276 } +1277 +1278 match last_packet_number { +1279 Some(last_packet_number) => { +1280 // Note that when sending in multiple spaces the last packet number will be +1281 // the one from the highest space. +1282 self.path_data_mut(path_id).congestion.on_sent( +1283 now, +1284 transmit.len() as u64, +1285 last_packet_number, +1286 ); +1287 PollPathStatus::Send(self.build_transmit(path_id, transmit)) +1288 } +1289 None => PollPathStatus::NothingToSend { congestion_blocked }, +1290 } +1291 } +1292 +1293 /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId). +1294 #[must_use] +1295 fn poll_transmit_path_space( +1296 &mut self, +1297 now: Instant, +1298 transmit: &mut TransmitBuf<'_>, +1299 path_id: PathId, +1300 space_id: SpaceId, +1301 remote_cid: ConnectionId, +1302 // If any other packet space has a usable path with PathStatus::Available. +1303 have_available_path: bool, +1304 // If we need to send a CONNECTION_CLOSE frame. +1305 connection_close_pending: bool, +1306 // Whether the current datagram needs to be padded to a certain size. +1307 mut pad_datagram: PadDatagram, +1308 ) -> PollPathSpaceStatus { +1309 // Keep track of the last packet number we wrote. If None we did not write any +1310 // packets. +1311 let mut last_packet_number = None; +1312 +1313 // Each loop of this may build one packet. It works logically as follows: +1314 // +1315 // - Check if something *needs* to be sent in this space and *can* be sent. +1316 // - If not, return to the caller who will call us again for the next space. +1317 // - Start a new datagram. +1318 // - Unless coalescing the packet into an existing datagram. +1319 // - Write the packet header and payload. +1320 // - Check if coalescing a next packet into the datagram is possible. +1321 // - If coalescing, finish packet without padding to leave space in the datagram. +1322 // - If not coalescing, complete the datagram: +1323 // - Finish packet with padding. +1324 // - Set the transmit segment size if this is the first datagram. +1325 // - Loop: next iteration will exit the loop if nothing more to send in this +1326 // space. The TransmitBuf will contain a started datagram with space if +1327 // coalescing, or completely filled datagram if not coalescing. +1328 loop { +1329 // Determine if anything can be sent in this packet number space (SpaceId + PathId). +1330 let max_packet_size = if transmit.datagram_remaining_mut() > 0 { +1331 // A datagram is started already, we are coalescing another packet into it. +1332 transmit.datagram_remaining_mut() +1333 } else { +1334 // A new datagram needs to be started. +1335 transmit.segment_size() +1336 }; +1337 let can_send = +1338 self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending); +1339 +1340 // Whether we would like to send any frames on this packet space. See the packet +1341 // scheduling described in poll_transmit. +1342 let space_should_send = { +1343 let path_exclusive_only = space_id == SpaceId::Data +1344 && have_available_path +1345 && self.path_data(path_id).local_status() == PathStatus::Backup; +1346 let path_should_send = if path_exclusive_only { +1347 can_send.path_exclusive +1348 } else { +1349 !can_send.is_empty() +1350 }; +1351 let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; +1352 path_should_send || needs_loss_probe +1353 }; +1354 +1355 if !space_should_send { +1356 // Nothing more to send. Previous iterations of this loop may have built +1357 // packets already. +1358 return match last_packet_number { +1359 Some(pn) => PollPathSpaceStatus::WrotePacket { +1360 last_packet_number: pn, +1361 pad_datagram, +1362 }, +1363 None => { +1364 // Only log for spaces which have crypto. +1365 if self.crypto_state.has_keys(space_id.encryption_level()) +1366 || (space_id == SpaceId::Data +1367 && self.crypto_state.has_keys(EncryptionLevel::ZeroRtt)) +1368 { +1369 trace!(?space_id, %path_id, "nothing to send in space"); +1370 } +1371 PollPathSpaceStatus::NothingToSend { +1372 congestion_blocked: false, 1373 } -1374 PollPathSpaceStatus::NothingToSend { -1375 congestion_blocked: false, -1376 } -1377 } -1378 }; -1379 } -1380 -1381 // We want to send on this space, check congestion control if we can. But only -1382 // if we will need to start a new datagram. If we are coalescing into an already -1383 // started datagram we do not need to check congestion control again. -1384 if transmit.datagram_remaining_mut() == 0 { -1385 let congestion_blocked = -1386 self.path_congestion_check(space_id, path_id, transmit, &can_send, now); -1387 if congestion_blocked != PathBlocked::No { -1388 // Previous iterations of this loop may have built packets already. -1389 return match last_packet_number { -1390 Some(pn) => PollPathSpaceStatus::WrotePacket { -1391 last_packet_number: pn, -1392 pad_datagram, -1393 }, -1394 None => { -1395 return PollPathSpaceStatus::NothingToSend { -1396 congestion_blocked: true, -1397 }; -1398 } -1399 }; -1400 } -1401 } -1402 -1403 // If the datagram is full (or there never was one started), we need to start a -1404 // new one. -1405 if transmit.datagram_remaining_mut() == 0 { -1406 if transmit.num_datagrams() >= transmit.max_datagrams().get() { -1407 // No more datagrams allowed. -1408 // Previous iterations of this loop may have built packets already. -1409 return match last_packet_number { -1410 Some(pn) => PollPathSpaceStatus::WrotePacket { -1411 last_packet_number: pn, -1412 pad_datagram, -1413 }, -1414 None => { -1415 return PollPathSpaceStatus::NothingToSend { -1416 congestion_blocked: false, -1417 }; -1418 } -1419 }; -1420 } -1421 -1422 match self.spaces[space_id].for_path(path_id).loss_probes { -1423 0 => transmit.start_new_datagram(), -1424 _ => { -1425 // We need something to send for a tail-loss probe. -1426 let request_immediate_ack = -1427 space_id == SpaceId::Data && self.peer_supports_ack_frequency(); -1428 self.spaces[space_id].maybe_queue_probe( -1429 path_id, -1430 request_immediate_ack, -1431 &self.streams, -1432 ); -1433 -1434 self.spaces[space_id].for_path(path_id).loss_probes -= 1; -1435 -1436 // Clamp the datagram to at most the minimum MTU to ensure that loss -1437 // probes can get through and enable recovery even if the path MTU -1438 // has shrank unexpectedly. -1439 transmit.start_new_datagram_with_size(std::cmp::min( -1440 usize::from(INITIAL_MTU), -1441 transmit.segment_size(), -1442 )); -1443 } -1444 } -1445 trace!(count = transmit.num_datagrams(), "new datagram started"); -1446 -1447 // We started a new datagram, we decide later if it needs padding. -1448 pad_datagram = PadDatagram::No; -1449 } -1450 -1451 // If coalescing another packet into the existing datagram, there should -1452 // still be enough space for a whole packet. -1453 if transmit.datagram_start_offset() < transmit.len() { -1454 debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE); -1455 } -1456 -1457 // -1458 // From here on, we've determined that a packet will definitely be sent. -1459 // -1460 -1461 if self.crypto_state.has_keys(EncryptionLevel::Initial) -1462 && space_id == SpaceId::Handshake -1463 && self.side.is_client() -1464 { -1465 // A client stops both sending and processing Initial packets when it -1466 // sends its first Handshake packet. -1467 self.discard_space(now, SpaceKind::Initial); +1374 } +1375 }; +1376 } +1377 +1378 // We want to send on this space, check congestion control if we can. But only +1379 // if we will need to start a new datagram. If we are coalescing into an already +1380 // started datagram we do not need to check congestion control again. +1381 if transmit.datagram_remaining_mut() == 0 { +1382 let congestion_blocked = +1383 self.path_congestion_check(space_id, path_id, transmit, &can_send, now); +1384 if congestion_blocked != PathBlocked::No { +1385 // Previous iterations of this loop may have built packets already. +1386 return match last_packet_number { +1387 Some(pn) => PollPathSpaceStatus::WrotePacket { +1388 last_packet_number: pn, +1389 pad_datagram, +1390 }, +1391 None => { +1392 return PollPathSpaceStatus::NothingToSend { +1393 congestion_blocked: true, +1394 }; +1395 } +1396 }; +1397 } +1398 } +1399 +1400 // If the datagram is full (or there never was one started), we need to start a +1401 // new one. +1402 if transmit.datagram_remaining_mut() == 0 { +1403 if transmit.num_datagrams() >= transmit.max_datagrams().get() { +1404 // No more datagrams allowed. +1405 // Previous iterations of this loop may have built packets already. +1406 return match last_packet_number { +1407 Some(pn) => PollPathSpaceStatus::WrotePacket { +1408 last_packet_number: pn, +1409 pad_datagram, +1410 }, +1411 None => { +1412 return PollPathSpaceStatus::NothingToSend { +1413 congestion_blocked: false, +1414 }; +1415 } +1416 }; +1417 } +1418 +1419 match self.spaces[space_id].for_path(path_id).loss_probes { +1420 0 => transmit.start_new_datagram(), +1421 _ => { +1422 // We need something to send for a tail-loss probe. +1423 let request_immediate_ack = +1424 space_id == SpaceId::Data && self.peer_supports_ack_frequency(); +1425 self.spaces[space_id].maybe_queue_probe( +1426 path_id, +1427 request_immediate_ack, +1428 &self.streams, +1429 ); +1430 +1431 self.spaces[space_id].for_path(path_id).loss_probes -= 1; +1432 +1433 // Clamp the datagram to at most the minimum MTU to ensure that loss +1434 // probes can get through and enable recovery even if the path MTU +1435 // has shrank unexpectedly. +1436 transmit.start_new_datagram_with_size(std::cmp::min( +1437 usize::from(INITIAL_MTU), +1438 transmit.segment_size(), +1439 )); +1440 } +1441 } +1442 trace!(count = transmit.num_datagrams(), "new datagram started"); +1443 +1444 // We started a new datagram, we decide later if it needs padding. +1445 pad_datagram = PadDatagram::No; +1446 } +1447 +1448 // If coalescing another packet into the existing datagram, there should +1449 // still be enough space for a whole packet. +1450 if transmit.datagram_start_offset() < transmit.len() { +1451 debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE); +1452 } +1453 +1454 // +1455 // From here on, we've determined that a packet will definitely be sent. +1456 // +1457 +1458 if self.crypto_state.has_keys(EncryptionLevel::Initial) +1459 && space_id == SpaceId::Handshake +1460 && self.side.is_client() +1461 { +1462 // A client stops both sending and processing Initial packets when it +1463 // sends its first Handshake packet. +1464 self.discard_space(now, SpaceKind::Initial); +1465 } +1466 if let Some(ref mut prev) = self.crypto_state.prev_crypto { +1467 prev.update_unacked = false; 1468 } -1469 if let Some(ref mut prev) = self.crypto_state.prev_crypto { -1470 prev.update_unacked = false; -1471 } -1472 -1473 let Some(mut builder) = PacketBuilder::new( -1474 now, -1475 space_id, -1476 path_id, -1477 remote_cid, -1478 transmit, -1479 can_send.other, -1480 self, -1481 ) else { -1482 // Confidentiality limit is exceeded and the connection has been killed. We -1483 // should not send any other packets. This works in a roundabout way: We -1484 // have started a datagram but not written anything into it. So even if we -1485 // get called again for another space we will see an already started -1486 // datagram and try and start another packet here. Then be stopped by the -1487 // same confidentiality limit. -1488 return PollPathSpaceStatus::NothingToSend { -1489 congestion_blocked: false, -1490 }; -1491 }; -1492 last_packet_number = Some(builder.packet_number); -1493 -1494 if space_id == SpaceId::Initial && (self.side.is_client() || can_send.other) { -1495 // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1 -1496 pad_datagram |= PadDatagram::ToMinMtu; +1469 +1470 let Some(mut builder) = PacketBuilder::new( +1471 now, +1472 space_id, +1473 path_id, +1474 remote_cid, +1475 transmit, +1476 can_send.other, +1477 self, +1478 ) else { +1479 // Confidentiality limit is exceeded and the connection has been killed. We +1480 // should not send any other packets. This works in a roundabout way: We +1481 // have started a datagram but not written anything into it. So even if we +1482 // get called again for another space we will see an already started +1483 // datagram and try and start another packet here. Then be stopped by the +1484 // same confidentiality limit. +1485 return PollPathSpaceStatus::NothingToSend { +1486 congestion_blocked: false, +1487 }; +1488 }; +1489 last_packet_number = Some(builder.packet_number); +1490 +1491 if space_id == SpaceId::Initial && (self.side.is_client() || can_send.other) { +1492 // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1 +1493 pad_datagram |= PadDatagram::ToMinMtu; +1494 } +1495 if space_id == SpaceId::Data && self.config.pad_to_mtu { +1496 pad_datagram |= PadDatagram::ToSegmentSize; 1497 } -1498 if space_id == SpaceId::Data && self.config.pad_to_mtu { -1499 pad_datagram |= PadDatagram::ToSegmentSize; -1500 } -1501 -1502 if can_send.close { -1503 trace!("sending CONNECTION_CLOSE"); -1504 // Encode ACKs before the ConnectionClose message, to give the receiver -1505 // a better approximate on what data has been processed. This is -1506 // especially important with ack delay, since the peer might not -1507 // have gotten any other ACK for the data earlier on. -1508 let is_multipath_negotiated = self.is_multipath_negotiated(); -1509 for path_id in self.spaces[space_id] -1510 .number_spaces -1511 .iter() -1512 .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty()) -1513 .map(|(&path_id, _)| path_id) -1514 .collect::<Vec<_>>() -1515 { -1516 Self::populate_acks( -1517 now, -1518 self.receiving_ecn, -1519 path_id, -1520 space_id, -1521 &mut self.spaces[space_id], -1522 is_multipath_negotiated, -1523 &mut builder, -1524 &mut self.stats.frame_tx, -1525 self.crypto_state.has_keys(space_id.encryption_level()), -1526 ); -1527 } -1528 -1529 // Since there only 64 ACK frames there will always be enough space -1530 // to encode the ConnectionClose frame too. However we still have the -1531 // check here to prevent crashes if something changes. -1532 -1533 // TODO(flub): This needs fixing for multipath, to ensure we can always -1534 // write the CONNECTION_CLOSE even if we have many PATH_ACKs to send: -1535 // https://github.com/n0-computer/quinn/issues/367. -1536 debug_assert!( -1537 builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND, -1538 "ACKs should leave space for ConnectionClose" -1539 ); -1540 let stats = &mut self.stats.frame_tx; -1541 if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() { -1542 let max_frame_size = builder.frame_space_remaining(); -1543 let close: Close = match self.state.as_type() { -1544 StateType::Closed => { -1545 let reason: Close = -1546 self.state.as_closed().expect("checked").clone().into(); -1547 if space_id == SpaceId::Data || reason.is_transport_layer() { -1548 reason -1549 } else { -1550 TransportError::APPLICATION_ERROR("").into() -1551 } -1552 } -1553 StateType::Draining => TransportError::NO_ERROR("").into(), -1554 _ => unreachable!( -1555 "tried to make a close packet when the connection wasn't closed" -1556 ), -1557 }; -1558 builder.write_frame(close.encoder(max_frame_size), stats); -1559 } -1560 let last_pn = builder.packet_number; -1561 builder.finish_and_track(now, self, path_id, pad_datagram); -1562 if space_id.kind() == self.highest_space { -1563 // Don't send another close packet. Even with multipath we only send -1564 // CONNECTION_CLOSE on a single path since we expect our paths to work. -1565 self.connection_close_pending = false; -1566 } -1567 // Send a close frame in every possible space for robustness, per -1568 // RFC9000 "Immediate Close during the Handshake". Don't bother trying -1569 // to send anything else. -1570 // TODO(flub): This breaks during the handshake if we can not coalesce -1571 // packets due to space reasons: the next space would either fail a -1572 // debug_assert checking for enough packet space or produce an invalid -1573 // packet. We need to keep track of per-space pending CONNECTION_CLOSE to -1574 // be able to send these across multiple calls to poll_transmit. Then -1575 // check for coalescing space here because initial packets need to be in -1576 // padded datagrams. And also add space checks for CONNECTION_CLOSE in -1577 // space_can_send so it would stop a GSO batch if the datagram is too -1578 // small for another CONNECTION_CLOSE packet. -1579 return PollPathSpaceStatus::WrotePacket { -1580 last_packet_number: last_pn, -1581 pad_datagram, -1582 }; -1583 } -1584 -1585 // If this boolean is true we only want to send frames which can not be sent on -1586 // any other path. See the path scheduling notes in Self::poll_transmit. -1587 let path_exclusive_only = -1588 have_available_path && self.path_data(path_id).local_status() == PathStatus::Backup; -1589 -1590 self.populate_packet(now, space_id, path_id, path_exclusive_only, &mut builder); -1591 -1592 // ACK-only packets should only be sent when explicitly allowed. If we write them due to -1593 // any other reason, there is a bug which leads to one component announcing write -1594 // readiness while not writing any data. This degrades performance. The condition is -1595 // only checked if the full MTU is available and when potentially large fixed-size -1596 // frames aren't queued, so that lack of space in the datagram isn't the reason for just -1597 // writing ACKs. -1598 debug_assert!( -1599 !(builder.sent_frames().is_ack_only(&self.streams) -1600 && !can_send.acks -1601 && can_send.other -1602 && builder.buf.segment_size() -1603 == self.path_data(path_id).current_mtu() as usize -1604 && self.datagrams.outgoing.is_empty()), -1605 "SendableFrames was {can_send:?}, but only ACKs have been written" -1606 ); -1607 if builder.sent_frames().requires_padding { -1608 pad_datagram |= PadDatagram::ToMinMtu; -1609 } -1610 -1611 for (path_id, _pn) in builder.sent_frames().largest_acked.iter() { -1612 self.spaces[space_id] -1613 .for_path(*path_id) -1614 .pending_acks -1615 .acks_sent(); -1616 self.timers.stop( -1617 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -1618 self.qlog.with_time(now), -1619 ); -1620 } -1621 -1622 // Now we need to finish the packet. Before we do so we need to know if we will -1623 // be coalescing the next packet into this one, or will be ending the datagram -1624 // as well. Because if this is the last packet in the datagram more padding -1625 // might be needed because of the packet type, or to fill the GSO segment size. -1626 -1627 // Are we allowed to coalesce AND is there enough space for another *packet* in -1628 // this datagram AND will we definitely send another packet? -1629 if builder.can_coalesce && path_id == PathId::ZERO && { -1630 let max_packet_size = builder -1631 .buf -1632 .datagram_remaining_mut() -1633 .saturating_sub(builder.predict_packet_end()); -1634 max_packet_size > MIN_PACKET_SPACE -1635 && self.has_pending_packet(space_id, max_packet_size, connection_close_pending) -1636 } { -1637 // We can append/coalesce the next packet into the current -1638 // datagram. Finish the current packet without adding extra padding. -1639 trace!("will coalesce with next packet"); -1640 builder.finish_and_track(now, self, path_id, PadDatagram::No); -1641 } else { -1642 // We need a new datagram for the next packet. Finish the current -1643 // packet with padding. -1644 // TODO(flub): if there isn't any more data to be sent, this will still pad -1645 // to the segment size and only discover there is nothing to send before -1646 // starting the next packet. That is wasting up to 32 bytes. -1647 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { -1648 // If too many padding bytes would be required to continue the -1649 // GSO batch after this packet, end the GSO batch here. Ensures -1650 // that fixed-size frames with heterogeneous sizes -1651 // (e.g. application datagrams) won't inadvertently waste large -1652 // amounts of bandwidth. The exact threshold is a bit arbitrary -1653 // and might benefit from further tuning, though there's no -1654 // universally optimal value. -1655 const MAX_PADDING: usize = 32; -1656 if builder.buf.datagram_remaining_mut() -1657 > builder.predict_packet_end() + MAX_PADDING -1658 { -1659 trace!( -1660 "GSO truncated by demand for {} padding bytes", -1661 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() -1662 ); -1663 let last_pn = builder.packet_number; -1664 builder.finish_and_track(now, self, path_id, PadDatagram::No); -1665 return PollPathSpaceStatus::Send { -1666 last_packet_number: last_pn, -1667 }; -1668 } -1669 -1670 // Pad the current datagram to GSO segment size so it can be -1671 // included in the GSO batch. -1672 builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize); -1673 } else { -1674 builder.finish_and_track(now, self, path_id, pad_datagram); -1675 } -1676 -1677 // If this is the first datagram we set the segment size to the size of the -1678 // first datagram. -1679 if transmit.num_datagrams() == 1 { -1680 transmit.clip_segment_size(); -1681 } -1682 } -1683 } -1684 } -1685 -1686 fn poll_transmit_mtu_probe( -1687 &mut self, -1688 now: Instant, -1689 buf: &mut Vec<u8>, -1690 path_id: PathId, -1691 ) -> Option<Transmit> { -1692 let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?; -1693 -1694 // We are definitely sending a DPLPMTUD probe. -1695 let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize); -1696 transmit.start_new_datagram_with_size(probe_size as usize); -1697 -1698 let mut builder = PacketBuilder::new( -1699 now, -1700 SpaceId::Data, -1701 path_id, -1702 active_cid, -1703 &mut transmit, -1704 true, -1705 self, -1706 )?; -1707 -1708 // We implement MTU probes as ping packets padded up to the probe size -1709 trace!(?probe_size, "writing MTUD probe"); -1710 builder.write_frame(frame::Ping, &mut self.stats.frame_tx); -1711 -1712 // If supported by the peer, we want no delays to the probe's ACK -1713 if self.peer_supports_ack_frequency() { -1714 builder.write_frame(frame::ImmediateAck, &mut self.stats.frame_tx); -1715 } -1716 -1717 builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size)); -1718 -1719 self.path_stats -1720 .entry(path_id) -1721 .or_default() -1722 .sent_plpmtud_probes += 1; +1498 +1499 if can_send.close { +1500 trace!("sending CONNECTION_CLOSE"); +1501 // Encode ACKs before the ConnectionClose message, to give the receiver +1502 // a better approximate on what data has been processed. This is +1503 // especially important with ack delay, since the peer might not +1504 // have gotten any other ACK for the data earlier on. +1505 let is_multipath_negotiated = self.is_multipath_negotiated(); +1506 for path_id in self.spaces[space_id] +1507 .number_spaces +1508 .iter() +1509 .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty()) +1510 .map(|(&path_id, _)| path_id) +1511 .collect::<Vec<_>>() +1512 { +1513 Self::populate_acks( +1514 now, +1515 self.receiving_ecn, +1516 path_id, +1517 space_id, +1518 &mut self.spaces[space_id], +1519 is_multipath_negotiated, +1520 &mut builder, +1521 &mut self.stats.frame_tx, +1522 self.crypto_state.has_keys(space_id.encryption_level()), +1523 ); +1524 } +1525 +1526 // Since there only 64 ACK frames there will always be enough space +1527 // to encode the ConnectionClose frame too. However we still have the +1528 // check here to prevent crashes if something changes. +1529 +1530 // TODO(flub): This needs fixing for multipath, to ensure we can always +1531 // write the CONNECTION_CLOSE even if we have many PATH_ACKs to send: +1532 // https://github.com/n0-computer/quinn/issues/367. +1533 debug_assert!( +1534 builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND, +1535 "ACKs should leave space for ConnectionClose" +1536 ); +1537 let stats = &mut self.stats.frame_tx; +1538 if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() { +1539 let max_frame_size = builder.frame_space_remaining(); +1540 let close: Close = match self.state.as_type() { +1541 StateType::Closed => { +1542 let reason: Close = +1543 self.state.as_closed().expect("checked").clone().into(); +1544 if space_id == SpaceId::Data || reason.is_transport_layer() { +1545 reason +1546 } else { +1547 TransportError::APPLICATION_ERROR("").into() +1548 } +1549 } +1550 StateType::Draining => TransportError::NO_ERROR("").into(), +1551 _ => unreachable!( +1552 "tried to make a close packet when the connection wasn't closed" +1553 ), +1554 }; +1555 builder.write_frame(close.encoder(max_frame_size), stats); +1556 } +1557 let last_pn = builder.packet_number; +1558 builder.finish_and_track(now, self, path_id, pad_datagram); +1559 if space_id.kind() == self.highest_space { +1560 // Don't send another close packet. Even with multipath we only send +1561 // CONNECTION_CLOSE on a single path since we expect our paths to work. +1562 self.connection_close_pending = false; +1563 } +1564 // Send a close frame in every possible space for robustness, per +1565 // RFC9000 "Immediate Close during the Handshake". Don't bother trying +1566 // to send anything else. +1567 // TODO(flub): This breaks during the handshake if we can not coalesce +1568 // packets due to space reasons: the next space would either fail a +1569 // debug_assert checking for enough packet space or produce an invalid +1570 // packet. We need to keep track of per-space pending CONNECTION_CLOSE to +1571 // be able to send these across multiple calls to poll_transmit. Then +1572 // check for coalescing space here because initial packets need to be in +1573 // padded datagrams. And also add space checks for CONNECTION_CLOSE in +1574 // space_can_send so it would stop a GSO batch if the datagram is too +1575 // small for another CONNECTION_CLOSE packet. +1576 return PollPathSpaceStatus::WrotePacket { +1577 last_packet_number: last_pn, +1578 pad_datagram, +1579 }; +1580 } +1581 +1582 // If this boolean is true we only want to send frames which can not be sent on +1583 // any other path. See the path scheduling notes in Self::poll_transmit. +1584 let path_exclusive_only = +1585 have_available_path && self.path_data(path_id).local_status() == PathStatus::Backup; +1586 +1587 self.populate_packet(now, space_id, path_id, path_exclusive_only, &mut builder); +1588 +1589 // ACK-only packets should only be sent when explicitly allowed. If we write them due to +1590 // any other reason, there is a bug which leads to one component announcing write +1591 // readiness while not writing any data. This degrades performance. The condition is +1592 // only checked if the full MTU is available and when potentially large fixed-size +1593 // frames aren't queued, so that lack of space in the datagram isn't the reason for just +1594 // writing ACKs. +1595 debug_assert!( +1596 !(builder.sent_frames().is_ack_only(&self.streams) +1597 && !can_send.acks +1598 && can_send.other +1599 && builder.buf.segment_size() +1600 == self.path_data(path_id).current_mtu() as usize +1601 && self.datagrams.outgoing.is_empty()), +1602 "SendableFrames was {can_send:?}, but only ACKs have been written" +1603 ); +1604 if builder.sent_frames().requires_padding { +1605 pad_datagram |= PadDatagram::ToMinMtu; +1606 } +1607 +1608 for (path_id, _pn) in builder.sent_frames().largest_acked.iter() { +1609 self.spaces[space_id] +1610 .for_path(*path_id) +1611 .pending_acks +1612 .acks_sent(); +1613 self.timers.stop( +1614 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +1615 self.qlog.with_time(now), +1616 ); +1617 } +1618 +1619 // Now we need to finish the packet. Before we do so we need to know if we will +1620 // be coalescing the next packet into this one, or will be ending the datagram +1621 // as well. Because if this is the last packet in the datagram more padding +1622 // might be needed because of the packet type, or to fill the GSO segment size. +1623 +1624 // Are we allowed to coalesce AND is there enough space for another *packet* in +1625 // this datagram AND will we definitely send another packet? +1626 if builder.can_coalesce && path_id == PathId::ZERO && { +1627 let max_packet_size = builder +1628 .buf +1629 .datagram_remaining_mut() +1630 .saturating_sub(builder.predict_packet_end()); +1631 max_packet_size > MIN_PACKET_SPACE +1632 && self.has_pending_packet(space_id, max_packet_size, connection_close_pending) +1633 } { +1634 // We can append/coalesce the next packet into the current +1635 // datagram. Finish the current packet without adding extra padding. +1636 trace!("will coalesce with next packet"); +1637 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1638 } else { +1639 // We need a new datagram for the next packet. Finish the current +1640 // packet with padding. +1641 // TODO(flub): if there isn't any more data to be sent, this will still pad +1642 // to the segment size and only discover there is nothing to send before +1643 // starting the next packet. That is wasting up to 32 bytes. +1644 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { +1645 // If too many padding bytes would be required to continue the +1646 // GSO batch after this packet, end the GSO batch here. Ensures +1647 // that fixed-size frames with heterogeneous sizes +1648 // (e.g. application datagrams) won't inadvertently waste large +1649 // amounts of bandwidth. The exact threshold is a bit arbitrary +1650 // and might benefit from further tuning, though there's no +1651 // universally optimal value. +1652 const MAX_PADDING: usize = 32; +1653 if builder.buf.datagram_remaining_mut() +1654 > builder.predict_packet_end() + MAX_PADDING +1655 { +1656 trace!( +1657 "GSO truncated by demand for {} padding bytes", +1658 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() +1659 ); +1660 let last_pn = builder.packet_number; +1661 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1662 return PollPathSpaceStatus::Send { +1663 last_packet_number: last_pn, +1664 }; +1665 } +1666 +1667 // Pad the current datagram to GSO segment size so it can be +1668 // included in the GSO batch. +1669 builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize); +1670 } else { +1671 builder.finish_and_track(now, self, path_id, pad_datagram); +1672 } +1673 +1674 // If this is the first datagram we set the segment size to the size of the +1675 // first datagram. +1676 if transmit.num_datagrams() == 1 { +1677 transmit.clip_segment_size(); +1678 } +1679 } +1680 } +1681 } +1682 +1683 fn poll_transmit_mtu_probe( +1684 &mut self, +1685 now: Instant, +1686 buf: &mut Vec<u8>, +1687 path_id: PathId, +1688 ) -> Option<Transmit> { +1689 let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?; +1690 +1691 // We are definitely sending a DPLPMTUD probe. +1692 let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize); +1693 transmit.start_new_datagram_with_size(probe_size as usize); +1694 +1695 let mut builder = PacketBuilder::new( +1696 now, +1697 SpaceId::Data, +1698 path_id, +1699 active_cid, +1700 &mut transmit, +1701 true, +1702 self, +1703 )?; +1704 +1705 // We implement MTU probes as ping packets padded up to the probe size +1706 trace!(?probe_size, "writing MTUD probe"); +1707 builder.write_frame(frame::Ping, &mut self.stats.frame_tx); +1708 +1709 // If supported by the peer, we want no delays to the probe's ACK +1710 if self.peer_supports_ack_frequency() { +1711 builder.write_frame(frame::ImmediateAck, &mut self.stats.frame_tx); +1712 } +1713 +1714 builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size)); +1715 +1716 self.path_stats +1717 .entry(path_id) +1718 .or_default() +1719 .sent_plpmtud_probes += 1; +1720 +1721 Some(self.build_transmit(path_id, transmit)) +1722 } 1723 -1724 Some(self.build_transmit(path_id, transmit)) -1725 } -1726 -1727 /// Returns the CID and probe size if a DPLPMTUD probe is needed. -1728 /// -1729 /// We MTU probe all paths for which all of the following is true: -1730 /// - We have an active destination CID for the path. -1731 /// - The remote address *and* path are validated. -1732 /// - The path is not abandoned. -1733 /// - The MTU Discovery subsystem wants to probe the path. -1734 fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> { -1735 let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?; -1736 let is_eligible = self.path_data(path_id).validated -1737 && !self.path_data(path_id).is_validating_path() -1738 && !self.abandoned_paths.contains(&path_id); -1739 -1740 if !is_eligible { -1741 return None; -1742 } -1743 let next_pn = self.spaces[SpaceId::Data] -1744 .for_path(path_id) -1745 .peek_tx_number(); -1746 let probe_size = self -1747 .path_data_mut(path_id) -1748 .mtud -1749 .poll_transmit(now, next_pn)?; +1724 /// Returns the CID and probe size if a DPLPMTUD probe is needed. +1725 /// +1726 /// We MTU probe all paths for which all of the following is true: +1727 /// - We have an active destination CID for the path. +1728 /// - The remote address *and* path are validated. +1729 /// - The path is not abandoned. +1730 /// - The MTU Discovery subsystem wants to probe the path. +1731 fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> { +1732 let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?; +1733 let is_eligible = self.path_data(path_id).validated +1734 && !self.path_data(path_id).is_validating_path() +1735 && !self.abandoned_paths.contains(&path_id); +1736 +1737 if !is_eligible { +1738 return None; +1739 } +1740 let next_pn = self.spaces[SpaceId::Data] +1741 .for_path(path_id) +1742 .peek_tx_number(); +1743 let probe_size = self +1744 .path_data_mut(path_id) +1745 .mtud +1746 .poll_transmit(now, next_pn)?; +1747 +1748 Some((active_cid, probe_size)) +1749 } 1750 -1751 Some((active_cid, probe_size)) -1752 } -1753 -1754 /// Returns true if there is a further packet to send on [`PathId::ZERO`]. -1755 /// -1756 /// In other words this is predicting whether the next call to -1757 /// [`Connection::space_can_send`] issued will return some frames to be sent. Including -1758 /// having to predict which packet number space it will be invoked with. This depends on -1759 /// how both [`Connection::poll_transmit_path`] and -1760 /// [`Connection::poll_transmit_path_space`] behave. -1761 /// -1762 /// This is needed to determine if packet coalescing can happen. Because the last packet -1763 /// in a datagram may need to be padded and thus we must know if another packet will -1764 /// follow or not. -1765 /// -1766 /// The next packet can be either in the same space, or in one of the following spaces -1767 /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and -1768 /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because -1769 /// packets are built from Initial to Handshake to Data spaces. -1770 fn has_pending_packet( -1771 &mut self, -1772 current_space_id: SpaceId, -1773 max_packet_size: usize, -1774 connection_close_pending: bool, -1775 ) -> bool { -1776 let mut space_id = current_space_id; -1777 loop { -1778 let can_send = self.space_can_send( -1779 space_id, -1780 PathId::ZERO, -1781 max_packet_size, -1782 connection_close_pending, -1783 ); -1784 if !can_send.is_empty() { -1785 return true; -1786 } -1787 match space_id.next() { -1788 Some(next_space_id) => space_id = next_space_id, -1789 None => break, -1790 } -1791 } -1792 false -1793 } -1794 -1795 /// Checks if creating a new datagram would be blocked by congestion control -1796 fn path_congestion_check( -1797 &mut self, -1798 space_id: SpaceId, -1799 path_id: PathId, -1800 transmit: &TransmitBuf<'_>, -1801 can_send: &SendableFrames, -1802 now: Instant, -1803 ) -> PathBlocked { -1804 // Anti-amplification is only based on `total_sent`, which gets updated after -1805 // the transmit is sent. Therefore we pass the amount of bytes for datagrams -1806 // that are already created, as well as 1 byte for starting another datagram. If -1807 // there is any anti-amplification budget left, we always allow a full MTU to be -1808 // sent (see https://github.com/quinn-rs/quinn/issues/1082). -1809 if self.side().is_server() -1810 && self -1811 .path_data(path_id) -1812 .anti_amplification_blocked(transmit.len() as u64 + 1) -1813 { -1814 trace!(?space_id, %path_id, "blocked by anti-amplification"); -1815 return PathBlocked::AntiAmplification; -1816 } -1817 -1818 // Congestion control check. -1819 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. -1820 let bytes_to_send = transmit.segment_size() as u64; -1821 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; -1822 -1823 if can_send.other && !need_loss_probe && !can_send.close { -1824 let path = self.path_data(path_id); -1825 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { -1826 trace!(?space_id, %path_id, "blocked by congestion control"); -1827 return PathBlocked::Congestion; -1828 } -1829 } -1830 -1831 // Pacing check. -1832 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { -1833 self.timers.set( -1834 Timer::PerPath(path_id, PathTimer::Pacing), -1835 delay, -1836 self.qlog.with_time(now), -1837 ); -1838 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though -1839 // they are not congestion controlled. -1840 trace!(?space_id, %path_id, "blocked by pacing"); -1841 return PathBlocked::Pacing; -1842 } +1751 /// Returns true if there is a further packet to send on [`PathId::ZERO`]. +1752 /// +1753 /// In other words this is predicting whether the next call to +1754 /// [`Connection::space_can_send`] issued will return some frames to be sent. Including +1755 /// having to predict which packet number space it will be invoked with. This depends on +1756 /// how both [`Connection::poll_transmit_path`] and +1757 /// [`Connection::poll_transmit_path_space`] behave. +1758 /// +1759 /// This is needed to determine if packet coalescing can happen. Because the last packet +1760 /// in a datagram may need to be padded and thus we must know if another packet will +1761 /// follow or not. +1762 /// +1763 /// The next packet can be either in the same space, or in one of the following spaces +1764 /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and +1765 /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because +1766 /// packets are built from Initial to Handshake to Data spaces. +1767 fn has_pending_packet( +1768 &mut self, +1769 current_space_id: SpaceId, +1770 max_packet_size: usize, +1771 connection_close_pending: bool, +1772 ) -> bool { +1773 let mut space_id = current_space_id; +1774 loop { +1775 let can_send = self.space_can_send( +1776 space_id, +1777 PathId::ZERO, +1778 max_packet_size, +1779 connection_close_pending, +1780 ); +1781 if !can_send.is_empty() { +1782 return true; +1783 } +1784 match space_id.next() { +1785 Some(next_space_id) => space_id = next_space_id, +1786 None => break, +1787 } +1788 } +1789 false +1790 } +1791 +1792 /// Checks if creating a new datagram would be blocked by congestion control +1793 fn path_congestion_check( +1794 &mut self, +1795 space_id: SpaceId, +1796 path_id: PathId, +1797 transmit: &TransmitBuf<'_>, +1798 can_send: &SendableFrames, +1799 now: Instant, +1800 ) -> PathBlocked { +1801 // Anti-amplification is only based on `total_sent`, which gets updated after +1802 // the transmit is sent. Therefore we pass the amount of bytes for datagrams +1803 // that are already created, as well as 1 byte for starting another datagram. If +1804 // there is any anti-amplification budget left, we always allow a full MTU to be +1805 // sent (see https://github.com/quinn-rs/quinn/issues/1082). +1806 if self.side().is_server() +1807 && self +1808 .path_data(path_id) +1809 .anti_amplification_blocked(transmit.len() as u64 + 1) +1810 { +1811 trace!(?space_id, %path_id, "blocked by anti-amplification"); +1812 return PathBlocked::AntiAmplification; +1813 } +1814 +1815 // Congestion control check. +1816 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. +1817 let bytes_to_send = transmit.segment_size() as u64; +1818 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; +1819 +1820 if can_send.other && !need_loss_probe && !can_send.close { +1821 let path = self.path_data(path_id); +1822 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { +1823 trace!(?space_id, %path_id, "blocked by congestion control"); +1824 return PathBlocked::Congestion; +1825 } +1826 } +1827 +1828 // Pacing check. +1829 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { +1830 self.timers.set( +1831 Timer::PerPath(path_id, PathTimer::Pacing), +1832 delay, +1833 self.qlog.with_time(now), +1834 ); +1835 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though +1836 // they are not congestion controlled. +1837 trace!(?space_id, %path_id, "blocked by pacing"); +1838 return PathBlocked::Pacing; +1839 } +1840 +1841 PathBlocked::No +1842 } 1843 -1844 PathBlocked::No -1845 } -1846 -1847 /// Send PATH_CHALLENGE for a previous path if necessary -1848 /// -1849 /// QUIC-TRANSPORT section 9.3.3 -1850 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> -1851 fn send_prev_path_challenge( -1852 &mut self, -1853 now: Instant, -1854 buf: &mut Vec<u8>, -1855 path_id: PathId, -1856 ) -> Option<Transmit> { -1857 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; -1858 // TODO (matheus23): We could use !prev_path.is_validating() here instead to -1859 // (possibly) also re-send challenges when they get lost. -1860 if !prev_path.send_new_challenge { -1861 return None; -1862 }; -1863 prev_path.send_new_challenge = false; -1864 let network_path = prev_path.network_path; -1865 let token = self.rng.random(); -1866 let info = paths::SentChallengeInfo { -1867 sent_instant: now, -1868 network_path, -1869 }; -1870 prev_path.challenges_sent.insert(token, info); -1871 debug_assert_eq!( -1872 self.highest_space, -1873 SpaceKind::Data, -1874 "PATH_CHALLENGE queued without 1-RTT keys" -1875 ); -1876 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); -1877 buf.start_new_datagram(); -1878 -1879 // Use the previous CID to avoid linking the new path with the previous path. We -1880 // don't bother accounting for possible retirement of that prev_cid because this is -1881 // sent once, immediately after migration, when the CID is known to be valid. Even -1882 // if a post-migration packet caused the CID to be retired, it's fair to pretend -1883 // this is sent first. -1884 let mut builder = -1885 PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, false, self)?; -1886 let challenge = frame::PathChallenge(token); -1887 let stats = &mut self.stats.frame_tx; -1888 builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path")); -1889 -1890 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame -1891 // to at least the smallest allowed maximum datagram size of 1200 bytes, -1892 // unless the anti-amplification limit for the path does not permit -1893 // sending a datagram of this size -1894 builder.pad_to(MIN_INITIAL_SIZE); -1895 -1896 builder.finish(self, now); -1897 self.stats.udp_tx.on_sent(1, buf.len()); -1898 self.path_stats -1899 .entry(path_id) -1900 .or_default() -1901 .udp_tx -1902 .on_sent(1, buf.len()); -1903 -1904 Some(Transmit { -1905 destination: network_path.remote, -1906 size: buf.len(), -1907 ecn: None, -1908 segment_size: None, -1909 src_ip: network_path.local_ip, -1910 }) -1911 } -1912 -1913 fn send_off_path_path_response( -1914 &mut self, -1915 now: Instant, -1916 buf: &mut Vec<u8>, -1917 path_id: PathId, -1918 ) -> Option<Transmit> { -1919 let path = self.paths.get_mut(&path_id).map(|state| &mut state.data)?; -1920 let cid_queue = self.remote_cids.get_mut(&path_id)?; -1921 let (token, network_path) = path.path_responses.pop_off_path(path.network_path)?; -1922 -1923 let cid = cid_queue -1924 .next_reserved() -1925 .unwrap_or_else(|| cid_queue.active()); -1926 // TODO(@divma): we should take a different approach when there is no fresh CID to use. -1927 // https://github.com/quinn-rs/quinn/issues/2184 -1928 -1929 let frame = frame::PathResponse(token); +1844 /// Send PATH_CHALLENGE for a previous path if necessary +1845 /// +1846 /// QUIC-TRANSPORT section 9.3.3 +1847 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> +1848 fn send_prev_path_challenge( +1849 &mut self, +1850 now: Instant, +1851 buf: &mut Vec<u8>, +1852 path_id: PathId, +1853 ) -> Option<Transmit> { +1854 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; +1855 // TODO (matheus23): We could use !prev_path.is_validating() here instead to +1856 // (possibly) also re-send challenges when they get lost. +1857 if !prev_path.send_new_challenge { +1858 return None; +1859 }; +1860 prev_path.send_new_challenge = false; +1861 let network_path = prev_path.network_path; +1862 let token = self.rng.random(); +1863 let info = paths::SentChallengeInfo { +1864 sent_instant: now, +1865 network_path, +1866 }; +1867 prev_path.challenges_sent.insert(token, info); +1868 debug_assert_eq!( +1869 self.highest_space, +1870 SpaceKind::Data, +1871 "PATH_CHALLENGE queued without 1-RTT keys" +1872 ); +1873 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); +1874 buf.start_new_datagram(); +1875 +1876 // Use the previous CID to avoid linking the new path with the previous path. We +1877 // don't bother accounting for possible retirement of that prev_cid because this is +1878 // sent once, immediately after migration, when the CID is known to be valid. Even +1879 // if a post-migration packet caused the CID to be retired, it's fair to pretend +1880 // this is sent first. +1881 let mut builder = +1882 PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, false, self)?; +1883 let challenge = frame::PathChallenge(token); +1884 let stats = &mut self.stats.frame_tx; +1885 builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path")); +1886 +1887 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame +1888 // to at least the smallest allowed maximum datagram size of 1200 bytes, +1889 // unless the anti-amplification limit for the path does not permit +1890 // sending a datagram of this size +1891 builder.pad_to(MIN_INITIAL_SIZE); +1892 +1893 builder.finish(self, now); +1894 self.stats.udp_tx.on_sent(1, buf.len()); +1895 self.path_stats +1896 .entry(path_id) +1897 .or_default() +1898 .udp_tx +1899 .on_sent(1, buf.len()); +1900 +1901 Some(Transmit { +1902 destination: network_path.remote, +1903 size: buf.len(), +1904 ecn: None, +1905 segment_size: None, +1906 src_ip: network_path.local_ip, +1907 }) +1908 } +1909 +1910 fn send_off_path_path_response( +1911 &mut self, +1912 now: Instant, +1913 buf: &mut Vec<u8>, +1914 path_id: PathId, +1915 ) -> Option<Transmit> { +1916 let path = self.paths.get_mut(&path_id).map(|state| &mut state.data)?; +1917 let cid_queue = self.remote_cids.get_mut(&path_id)?; +1918 let (token, network_path) = path.path_responses.pop_off_path(path.network_path)?; +1919 +1920 let cid = cid_queue +1921 .next_reserved() +1922 .unwrap_or_else(|| cid_queue.active()); +1923 // TODO(@divma): we should take a different approach when there is no fresh CID to use. +1924 // https://github.com/quinn-rs/quinn/issues/2184 +1925 +1926 let frame = frame::PathResponse(token); +1927 +1928 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); +1929 buf.start_new_datagram(); 1930 -1931 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); -1932 buf.start_new_datagram(); -1933 -1934 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, false, self)?; -1935 let stats = &mut self.stats.frame_tx; -1936 builder.write_frame_with_log_msg(frame, stats, Some("(off-path)")); -1937 builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu); -1938 -1939 let size = buf.len(); -1940 -1941 self.stats.udp_tx.on_sent(1, size); -1942 self.path_stats -1943 .entry(path_id) -1944 .or_default() -1945 .udp_tx -1946 .on_sent(1, size); -1947 Some(Transmit { -1948 destination: network_path.remote, -1949 size, -1950 ecn: None, -1951 segment_size: None, -1952 src_ip: network_path.local_ip, -1953 }) -1954 } -1955 -1956 /// Send a nat traversal challenge (off-path) on this path if possible. -1957 /// -1958 /// This will ensure the path still has a remaining CID to use if the active one should be -1959 /// retired. -1960 fn send_nat_traversal_path_challenge( -1961 &mut self, -1962 now: Instant, -1963 buf: &mut Vec<u8>, -1964 path_id: PathId, -1965 ) -> Option<Transmit> { -1966 let server_side = self.iroh_hp.server_side_mut().ok()?; -1967 let probe = server_side.next_probe()?; -1968 if !self.paths.get(&path_id)?.data.validated { -1969 // Path is not usable for probing -1970 return None; -1971 } -1972 -1973 let remote_cids = self.remote_cids.get_mut(&path_id)?; -1974 -1975 // Check if this path has enough CIDs to send a probe. One to be reserved, one in case the -1976 // active CID needs to be retired. -1977 if remote_cids.remaining() < 2 { -1978 return None; -1979 } -1980 -1981 let cid = remote_cids.next_reserved()?; -1982 let remote = probe.remote(); -1983 let token = self.rng.random(); -1984 probe.mark_as_sent(); -1985 -1986 let frame = frame::PathChallenge(token); +1931 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, false, self)?; +1932 let stats = &mut self.stats.frame_tx; +1933 builder.write_frame_with_log_msg(frame, stats, Some("(off-path)")); +1934 builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu); +1935 +1936 let size = buf.len(); +1937 +1938 self.stats.udp_tx.on_sent(1, size); +1939 self.path_stats +1940 .entry(path_id) +1941 .or_default() +1942 .udp_tx +1943 .on_sent(1, size); +1944 Some(Transmit { +1945 destination: network_path.remote, +1946 size, +1947 ecn: None, +1948 segment_size: None, +1949 src_ip: network_path.local_ip, +1950 }) +1951 } +1952 +1953 /// Send a nat traversal challenge (off-path) on this path if possible. +1954 /// +1955 /// This will ensure the path still has a remaining CID to use if the active one should be +1956 /// retired. +1957 fn send_nat_traversal_path_challenge( +1958 &mut self, +1959 now: Instant, +1960 buf: &mut Vec<u8>, +1961 path_id: PathId, +1962 ) -> Option<Transmit> { +1963 let server_side = self.iroh_hp.server_side_mut().ok()?; +1964 let probe = server_side.next_probe()?; +1965 if !self.paths.get(&path_id)?.data.validated { +1966 // Path is not usable for probing +1967 return None; +1968 } +1969 +1970 let remote_cids = self.remote_cids.get_mut(&path_id)?; +1971 +1972 // Check if this path has enough CIDs to send a probe. One to be reserved, one in case the +1973 // active CID needs to be retired. +1974 if remote_cids.remaining() < 2 { +1975 return None; +1976 } +1977 +1978 let cid = remote_cids.next_reserved()?; +1979 let remote = probe.remote(); +1980 let token = self.rng.random(); +1981 probe.mark_as_sent(); +1982 +1983 let frame = frame::PathChallenge(token); +1984 +1985 let mut buf = TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); +1986 buf.start_new_datagram(); 1987 -1988 let mut buf = TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); -1989 buf.start_new_datagram(); -1990 -1991 let mut builder = -1992 PacketBuilder::new(now, SpaceId::Data, path_id, cid, &mut buf, false, self)?; -1993 let stats = &mut self.stats.frame_tx; -1994 builder.write_frame_with_log_msg(frame, stats, Some("(nat-traversal)")); -1995 builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu); -1996 -1997 let path = &mut self.paths.get_mut(&path_id).expect("checked").data; -1998 -1999 path.challenges_sent.insert( -2000 token, -2001 paths::SentChallengeInfo { -2002 sent_instant: now, -2003 network_path: FourTuple { -2004 remote, -2005 local_ip: None, -2006 }, -2007 }, -2008 ); -2009 -2010 let size = buf.len(); -2011 -2012 self.stats.udp_tx.on_sent(1, size); -2013 self.path_stats -2014 .entry(path_id) -2015 .or_default() -2016 .udp_tx -2017 .on_sent(1, size); -2018 -2019 Some(Transmit { -2020 destination: remote, -2021 size, -2022 ecn: None, -2023 segment_size: None, -2024 src_ip: None, -2025 }) -2026 } -2027 -2028 /// Indicate what types of frames are ready to send for the given space -2029 /// -2030 /// *packet_size* is the number of bytes available to build the next packet. -2031 /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be -2032 /// sent. -2033 fn space_can_send( -2034 &mut self, -2035 space_id: SpaceId, -2036 path_id: PathId, -2037 packet_size: usize, -2038 connection_close_pending: bool, -2039 ) -> SendableFrames { -2040 let space = &mut self.spaces[space_id]; -2041 let space_has_crypto = self.crypto_state.has_keys(space_id.encryption_level()); -2042 -2043 if !space_has_crypto -2044 && (space_id != SpaceId::Data -2045 || !self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) -2046 || self.side.is_server()) -2047 { -2048 // Nothing to send in this space -2049 return SendableFrames::empty(); -2050 } -2051 -2052 let mut can_send = space.can_send(path_id, &self.streams); -2053 -2054 // Check for 1RTT space. -2055 if space_id == SpaceId::Data { -2056 let pn = space.for_path(path_id).peek_tx_number(); -2057 // Number of bytes available for frames if this is a 1-RTT packet. We're -2058 // guaranteed to be able to send an individual frame at least this large in the -2059 // next 1-RTT packet. This could be generalized to support every space, but it's -2060 // only needed to handle large fixed-size frames, which only exist in 1-RTT -2061 // (application datagrams). -2062 let frame_space_1rtt = -2063 packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); -2064 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); -2065 } -2066 -2067 can_send.close = connection_close_pending && space_has_crypto; +1988 let mut builder = +1989 PacketBuilder::new(now, SpaceId::Data, path_id, cid, &mut buf, false, self)?; +1990 let stats = &mut self.stats.frame_tx; +1991 builder.write_frame_with_log_msg(frame, stats, Some("(nat-traversal)")); +1992 builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu); +1993 +1994 let path = &mut self.paths.get_mut(&path_id).expect("checked").data; +1995 +1996 path.challenges_sent.insert( +1997 token, +1998 paths::SentChallengeInfo { +1999 sent_instant: now, +2000 network_path: FourTuple { +2001 remote, +2002 local_ip: None, +2003 }, +2004 }, +2005 ); +2006 +2007 let size = buf.len(); +2008 +2009 self.stats.udp_tx.on_sent(1, size); +2010 self.path_stats +2011 .entry(path_id) +2012 .or_default() +2013 .udp_tx +2014 .on_sent(1, size); +2015 +2016 Some(Transmit { +2017 destination: remote, +2018 size, +2019 ecn: None, +2020 segment_size: None, +2021 src_ip: None, +2022 }) +2023 } +2024 +2025 /// Indicate what types of frames are ready to send for the given space +2026 /// +2027 /// *packet_size* is the number of bytes available to build the next packet. +2028 /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be +2029 /// sent. +2030 fn space_can_send( +2031 &mut self, +2032 space_id: SpaceId, +2033 path_id: PathId, +2034 packet_size: usize, +2035 connection_close_pending: bool, +2036 ) -> SendableFrames { +2037 let space = &mut self.spaces[space_id]; +2038 let space_has_crypto = self.crypto_state.has_keys(space_id.encryption_level()); +2039 +2040 if !space_has_crypto +2041 && (space_id != SpaceId::Data +2042 || !self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) +2043 || self.side.is_server()) +2044 { +2045 // Nothing to send in this space +2046 return SendableFrames::empty(); +2047 } +2048 +2049 let mut can_send = space.can_send(path_id, &self.streams); +2050 +2051 // Check for 1RTT space. +2052 if space_id == SpaceId::Data { +2053 let pn = space.for_path(path_id).peek_tx_number(); +2054 // Number of bytes available for frames if this is a 1-RTT packet. We're +2055 // guaranteed to be able to send an individual frame at least this large in the +2056 // next 1-RTT packet. This could be generalized to support every space, but it's +2057 // only needed to handle large fixed-size frames, which only exist in 1-RTT +2058 // (application datagrams). +2059 let frame_space_1rtt = +2060 packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); +2061 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); +2062 } +2063 +2064 can_send.close = connection_close_pending && space_has_crypto; +2065 +2066 can_send +2067 } 2068 -2069 can_send -2070 } -2071 -2072 /// Process `ConnectionEvent`s generated by the associated `Endpoint` -2073 /// -2074 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals -2075 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be -2076 /// extracted through the relevant methods. -2077 pub fn handle_event(&mut self, event: ConnectionEvent) { -2078 use ConnectionEventInner::*; -2079 match event.0 { -2080 Datagram(DatagramConnectionEvent { -2081 now, -2082 network_path, -2083 path_id, -2084 ecn, -2085 first_decode, -2086 remaining, -2087 }) => { -2088 let span = trace_span!("pkt", %path_id); -2089 let _guard = span.enter(); -2090 -2091 if self.update_network_path_or_discard(network_path, path_id) { -2092 // A return value of true indicates we should discard this packet. -2093 return; -2094 } -2095 -2096 let was_anti_amplification_blocked = self -2097 .path(path_id) -2098 .map(|path| path.anti_amplification_blocked(1)) -2099 // We never tried to send on an non-existing (new) path so have not been -2100 // anti-amplification blocked for it previously. -2101 .unwrap_or(false); -2102 -2103 self.stats.udp_rx.datagrams += 1; -2104 self.stats.udp_rx.bytes += first_decode.len() as u64; -2105 let rx = &mut self.path_stats.entry(path_id).or_default().udp_rx; -2106 rx.datagrams += 1; -2107 rx.bytes += first_decode.len() as u64; -2108 let data_len = first_decode.len(); -2109 -2110 self.handle_decode(now, network_path, path_id, ecn, first_decode); -2111 // The current `path` might have changed inside `handle_decode` since the packet -2112 // could have triggered a migration. The packet might also belong to an unknown -2113 // path and have been rejected. Make sure the data received is accounted for the -2114 // most recent path by accessing `path` after `handle_decode`. -2115 if let Some(path) = self.path_mut(path_id) { -2116 path.inc_total_recvd(data_len as u64); -2117 } -2118 -2119 if let Some(data) = remaining { -2120 self.stats.udp_rx.bytes += data.len() as u64; -2121 self.path_stats.entry(path_id).or_default().udp_rx.bytes += data.len() as u64; -2122 self.handle_coalesced(now, network_path, path_id, ecn, data); -2123 } -2124 -2125 if let Some(path) = self.paths.get_mut(&path_id) { -2126 self.qlog -2127 .emit_recovery_metrics(path_id, &mut path.data, now); -2128 } -2129 -2130 if was_anti_amplification_blocked { -2131 // A prior attempt to set the loss detection timer may have failed due to -2132 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if -2133 // the server's first flight is lost. -2134 self.set_loss_detection_timer(now, path_id); -2135 } -2136 } -2137 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { -2138 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); -2139 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); -2140 let cid_state = self -2141 .local_cid_state -2142 .entry(path_id) -2143 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); -2144 cid_state.new_cids(&ids, now); -2145 -2146 ids.into_iter().rev().for_each(|frame| { -2147 self.spaces[SpaceId::Data].pending.new_cids.push(frame); -2148 }); -2149 // Always update Timer::PushNewCid -2150 self.reset_cid_retirement(now); -2151 } -2152 } -2153 } -2154 -2155 /// Updates the network path for `path_id`. -2156 /// -2157 /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded. -2158 /// Returns false if the path was updated and the packet doesn't need to be discarded. -2159 fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool { -2160 let remote_may_migrate = self.side.remote_may_migrate(&self.state); -2161 let local_ip_may_migrate = self.side.is_client(); -2162 // If this packet could initiate a migration and we're a client or a server that -2163 // forbids migration, drop the datagram. This could be relaxed to heuristically -2164 // permit NAT-rebinding-like migration. -2165 if let Some(known_path) = self.path_mut(path_id) { -2166 if network_path.remote != known_path.network_path.remote && !remote_may_migrate { -2167 trace!( -2168 %path_id, -2169 %network_path, -2170 %known_path.network_path, -2171 "discarding packet from unrecognized peer" -2172 ); -2173 return true; -2174 } -2175 -2176 if known_path.network_path.local_ip.is_some() -2177 && network_path.local_ip.is_some() -2178 && known_path.network_path.local_ip != network_path.local_ip -2179 && !local_ip_may_migrate -2180 { -2181 trace!( -2182 %path_id, -2183 %network_path, -2184 %known_path.network_path, -2185 "discarding packet sent to incorrect interface" -2186 ); -2187 return true; -2188 } -2189 // If the datagram indicates that we've changed our local IP, we update it. -2190 // This is alluded to in Section 5.2 of the Multipath RFC draft 18: -2191 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the -2192 // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1. -2193 if let Some(local_ip) = network_path.local_ip { -2194 if known_path -2195 .network_path -2196 .local_ip -2197 .is_some_and(|ip| ip != local_ip) -2198 { -2199 debug!( -2200 %path_id, -2201 %network_path, -2202 %known_path.network_path, -2203 "path's local address seemingly migrated" -2204 ); -2205 } -2206 // We update the address without path validation on the client side. -2207 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1 -2208 // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]). -2209 // This sounds like it's *only* the server endpoints that do this. -2210 // TODO(matheus23): We should still consider doing a proper migration on the client side in the future. -2211 // For now, this preserves the behavior of this code pre 4-tuple tracking. -2212 known_path.network_path.local_ip = Some(local_ip); -2213 } -2214 } -2215 false -2216 } -2217 -2218 /// Process timer expirations -2219 /// -2220 /// Executes protocol logic, potentially preparing signals (including application `Event`s, -2221 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant -2222 /// methods. -2223 /// -2224 /// It is most efficient to call this immediately after the system clock reaches the latest -2225 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply -2226 /// no-op and therefore are safe. -2227 pub fn handle_timeout(&mut self, now: Instant) { -2228 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { -2229 // TODO(@divma): remove `at` when the unicorn is born -2230 trace!(?timer, at=?now, "timeout"); -2231 match timer { -2232 Timer::Conn(timer) => match timer { -2233 ConnTimer::Close => { -2234 self.state.move_to_drained(None); -2235 self.endpoint_events.push_back(EndpointEventInner::Drained); +2069 /// Process `ConnectionEvent`s generated by the associated `Endpoint` +2070 /// +2071 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals +2072 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be +2073 /// extracted through the relevant methods. +2074 pub fn handle_event(&mut self, event: ConnectionEvent) { +2075 use ConnectionEventInner::*; +2076 match event.0 { +2077 Datagram(DatagramConnectionEvent { +2078 now, +2079 network_path, +2080 path_id, +2081 ecn, +2082 first_decode, +2083 remaining, +2084 }) => { +2085 let span = trace_span!("pkt", %path_id); +2086 let _guard = span.enter(); +2087 +2088 if self.update_network_path_or_discard(network_path, path_id) { +2089 // A return value of true indicates we should discard this packet. +2090 return; +2091 } +2092 +2093 let was_anti_amplification_blocked = self +2094 .path(path_id) +2095 .map(|path| path.anti_amplification_blocked(1)) +2096 // We never tried to send on an non-existing (new) path so have not been +2097 // anti-amplification blocked for it previously. +2098 .unwrap_or(false); +2099 +2100 self.stats.udp_rx.datagrams += 1; +2101 self.stats.udp_rx.bytes += first_decode.len() as u64; +2102 let rx = &mut self.path_stats.entry(path_id).or_default().udp_rx; +2103 rx.datagrams += 1; +2104 rx.bytes += first_decode.len() as u64; +2105 let data_len = first_decode.len(); +2106 +2107 self.handle_decode(now, network_path, path_id, ecn, first_decode); +2108 // The current `path` might have changed inside `handle_decode` since the packet +2109 // could have triggered a migration. The packet might also belong to an unknown +2110 // path and have been rejected. Make sure the data received is accounted for the +2111 // most recent path by accessing `path` after `handle_decode`. +2112 if let Some(path) = self.path_mut(path_id) { +2113 path.inc_total_recvd(data_len as u64); +2114 } +2115 +2116 if let Some(data) = remaining { +2117 self.stats.udp_rx.bytes += data.len() as u64; +2118 self.path_stats.entry(path_id).or_default().udp_rx.bytes += data.len() as u64; +2119 self.handle_coalesced(now, network_path, path_id, ecn, data); +2120 } +2121 +2122 if let Some(path) = self.paths.get_mut(&path_id) { +2123 self.qlog +2124 .emit_recovery_metrics(path_id, &mut path.data, now); +2125 } +2126 +2127 if was_anti_amplification_blocked { +2128 // A prior attempt to set the loss detection timer may have failed due to +2129 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if +2130 // the server's first flight is lost. +2131 self.set_loss_detection_timer(now, path_id); +2132 } +2133 } +2134 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { +2135 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); +2136 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); +2137 let cid_state = self +2138 .local_cid_state +2139 .entry(path_id) +2140 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); +2141 cid_state.new_cids(&ids, now); +2142 +2143 ids.into_iter().rev().for_each(|frame| { +2144 self.spaces[SpaceId::Data].pending.new_cids.push(frame); +2145 }); +2146 // Always update Timer::PushNewCid +2147 self.reset_cid_retirement(now); +2148 } +2149 } +2150 } +2151 +2152 /// Updates the network path for `path_id`. +2153 /// +2154 /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded. +2155 /// Returns false if the path was updated and the packet doesn't need to be discarded. +2156 fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool { +2157 let remote_may_migrate = self.side.remote_may_migrate(&self.state); +2158 let local_ip_may_migrate = self.side.is_client(); +2159 // If this packet could initiate a migration and we're a client or a server that +2160 // forbids migration, drop the datagram. This could be relaxed to heuristically +2161 // permit NAT-rebinding-like migration. +2162 if let Some(known_path) = self.path_mut(path_id) { +2163 if network_path.remote != known_path.network_path.remote && !remote_may_migrate { +2164 trace!( +2165 %path_id, +2166 %network_path, +2167 %known_path.network_path, +2168 "discarding packet from unrecognized peer" +2169 ); +2170 return true; +2171 } +2172 +2173 if known_path.network_path.local_ip.is_some() +2174 && network_path.local_ip.is_some() +2175 && known_path.network_path.local_ip != network_path.local_ip +2176 && !local_ip_may_migrate +2177 { +2178 trace!( +2179 %path_id, +2180 %network_path, +2181 %known_path.network_path, +2182 "discarding packet sent to incorrect interface" +2183 ); +2184 return true; +2185 } +2186 // If the datagram indicates that we've changed our local IP, we update it. +2187 // This is alluded to in Section 5.2 of the Multipath RFC draft 18: +2188 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the +2189 // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1. +2190 if let Some(local_ip) = network_path.local_ip { +2191 if known_path +2192 .network_path +2193 .local_ip +2194 .is_some_and(|ip| ip != local_ip) +2195 { +2196 debug!( +2197 %path_id, +2198 %network_path, +2199 %known_path.network_path, +2200 "path's local address seemingly migrated" +2201 ); +2202 } +2203 // We update the address without path validation on the client side. +2204 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1 +2205 // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]). +2206 // This sounds like it's *only* the server endpoints that do this. +2207 // TODO(matheus23): We should still consider doing a proper migration on the client side in the future. +2208 // For now, this preserves the behavior of this code pre 4-tuple tracking. +2209 known_path.network_path.local_ip = Some(local_ip); +2210 } +2211 } +2212 false +2213 } +2214 +2215 /// Process timer expirations +2216 /// +2217 /// Executes protocol logic, potentially preparing signals (including application `Event`s, +2218 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant +2219 /// methods. +2220 /// +2221 /// It is most efficient to call this immediately after the system clock reaches the latest +2222 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply +2223 /// no-op and therefore are safe. +2224 pub fn handle_timeout(&mut self, now: Instant) { +2225 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { +2226 // TODO(@divma): remove `at` when the unicorn is born +2227 trace!(?timer, at=?now, "timeout"); +2228 match timer { +2229 Timer::Conn(timer) => match timer { +2230 ConnTimer::Close => { +2231 self.state.move_to_drained(None); +2232 self.endpoint_events.push_back(EndpointEventInner::Drained); +2233 } +2234 ConnTimer::Idle => { +2235 self.kill(ConnectionError::TimedOut); 2236 } -2237 ConnTimer::Idle => { -2238 self.kill(ConnectionError::TimedOut); -2239 } -2240 ConnTimer::KeepAlive => { -2241 trace!("sending keep-alive"); -2242 self.ping(); +2237 ConnTimer::KeepAlive => { +2238 trace!("sending keep-alive"); +2239 self.ping(); +2240 } +2241 ConnTimer::KeyDiscard => { +2242 self.crypto_state.discard_temporary_keys(); 2243 } -2244 ConnTimer::KeyDiscard => { -2245 self.crypto_state.discard_temporary_keys(); -2246 } -2247 ConnTimer::PushNewCid => { -2248 while let Some((path_id, when)) = self.next_cid_retirement() { -2249 if when > now { -2250 break; -2251 } -2252 match self.local_cid_state.get_mut(&path_id) { -2253 None => error!(%path_id, "No local CID state for path"), -2254 Some(cid_state) => { -2255 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame -2256 let num_new_cid = cid_state.on_cid_timeout().into(); -2257 if !self.state.is_closed() { -2258 trace!( -2259 "push a new CID to peer RETIRE_PRIOR_TO field {}", -2260 cid_state.retire_prior_to() -2261 ); -2262 self.endpoint_events.push_back( -2263 EndpointEventInner::NeedIdentifiers( -2264 path_id, -2265 now, -2266 num_new_cid, -2267 ), -2268 ); -2269 } -2270 } -2271 } -2272 } -2273 } -2274 }, -2275 // TODO: add path_id as span somehow -2276 Timer::PerPath(path_id, timer) => { -2277 let span = trace_span!("per-path timer fired", %path_id, ?timer); -2278 let _guard = span.enter(); -2279 match timer { -2280 PathTimer::PathIdle => { -2281 if let Err(err) = self.close_path( -2282 now, -2283 path_id, -2284 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), -2285 ) { -2286 warn!(?err, "failed closing path"); -2287 } -2288 } -2289 -2290 PathTimer::PathKeepAlive => { -2291 trace!("sending keep-alive on path"); -2292 self.ping_path(path_id).ok(); -2293 } -2294 PathTimer::LossDetection => { -2295 self.on_loss_detection_timeout(now, path_id); -2296 self.qlog.emit_recovery_metrics( -2297 path_id, -2298 &mut self.paths.get_mut(&path_id).unwrap().data, -2299 now, -2300 ); -2301 } -2302 PathTimer::PathValidation => { -2303 let Some(path) = self.paths.get_mut(&path_id) else { -2304 continue; -2305 }; -2306 self.timers.stop( -2307 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -2308 self.qlog.with_time(now), -2309 ); -2310 debug!("path validation failed"); -2311 if let Some((_, prev)) = path.prev.take() { -2312 path.data = prev; -2313 } -2314 path.data.challenges_sent.clear(); -2315 path.data.send_new_challenge = false; -2316 } -2317 PathTimer::PathChallengeLost => { -2318 let Some(path) = self.paths.get_mut(&path_id) else { -2319 continue; -2320 }; -2321 trace!("path challenge deemed lost"); -2322 path.data.send_new_challenge = true; -2323 } -2324 PathTimer::PathOpen => { -2325 let Some(path) = self.paths.get_mut(&path_id) else { -2326 continue; -2327 }; -2328 path.data.challenges_sent.clear(); -2329 path.data.send_new_challenge = false; -2330 self.timers.stop( -2331 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -2332 self.qlog.with_time(now), -2333 ); -2334 debug!("new path validation failed"); -2335 if let Err(err) = self.close_path( -2336 now, -2337 path_id, -2338 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), -2339 ) { -2340 warn!(?err, "failed closing path"); -2341 } -2342 -2343 self.events.push_back(Event::Path(PathEvent::LocallyClosed { -2344 id: path_id, -2345 error: PathError::ValidationFailed, -2346 })); -2347 } -2348 PathTimer::Pacing => trace!("pacing timer expired"), -2349 PathTimer::MaxAckDelay => { -2350 trace!("max ack delay reached"); -2351 // This timer is only armed in the Data space -2352 self.spaces[SpaceId::Data] -2353 .for_path(path_id) -2354 .pending_acks -2355 .on_max_ack_delay_timeout() -2356 } -2357 PathTimer::DiscardPath => { -2358 // The path was abandoned and 3*PTO has expired since. Clean up all -2359 // remaining state and install stateless reset token. -2360 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); -2361 if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) { -2362 let (min_seq, max_seq) = local_cid_state.active_seq(); -2363 for seq in min_seq..=max_seq { -2364 self.endpoint_events.push_back( -2365 EndpointEventInner::RetireConnectionId( -2366 now, path_id, seq, false, -2367 ), -2368 ); -2369 } -2370 } -2371 self.discard_path(path_id, now); -2372 } -2373 } -2374 } -2375 } -2376 } -2377 } -2378 -2379 /// Close a connection immediately -2380 /// -2381 /// This does not ensure delivery of outstanding data. It is the application's responsibility to -2382 /// call this only when all important communications have been completed, e.g. by calling -2383 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding -2384 /// [`StreamEvent::Finished`] event. +2244 ConnTimer::PushNewCid => { +2245 while let Some((path_id, when)) = self.next_cid_retirement() { +2246 if when > now { +2247 break; +2248 } +2249 match self.local_cid_state.get_mut(&path_id) { +2250 None => error!(%path_id, "No local CID state for path"), +2251 Some(cid_state) => { +2252 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame +2253 let num_new_cid = cid_state.on_cid_timeout().into(); +2254 if !self.state.is_closed() { +2255 trace!( +2256 "push a new CID to peer RETIRE_PRIOR_TO field {}", +2257 cid_state.retire_prior_to() +2258 ); +2259 self.endpoint_events.push_back( +2260 EndpointEventInner::NeedIdentifiers( +2261 path_id, +2262 now, +2263 num_new_cid, +2264 ), +2265 ); +2266 } +2267 } +2268 } +2269 } +2270 } +2271 }, +2272 // TODO: add path_id as span somehow +2273 Timer::PerPath(path_id, timer) => { +2274 let span = trace_span!("per-path timer fired", %path_id, ?timer); +2275 let _guard = span.enter(); +2276 match timer { +2277 PathTimer::PathIdle => { +2278 if let Err(err) = self.close_path( +2279 now, +2280 path_id, +2281 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), +2282 ) { +2283 warn!(?err, "failed closing path"); +2284 } +2285 } +2286 +2287 PathTimer::PathKeepAlive => { +2288 trace!("sending keep-alive on path"); +2289 self.ping_path(path_id).ok(); +2290 } +2291 PathTimer::LossDetection => { +2292 self.on_loss_detection_timeout(now, path_id); +2293 self.qlog.emit_recovery_metrics( +2294 path_id, +2295 &mut self.paths.get_mut(&path_id).unwrap().data, +2296 now, +2297 ); +2298 } +2299 PathTimer::PathValidation => { +2300 let Some(path) = self.paths.get_mut(&path_id) else { +2301 continue; +2302 }; +2303 self.timers.stop( +2304 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +2305 self.qlog.with_time(now), +2306 ); +2307 debug!("path validation failed"); +2308 if let Some((_, prev)) = path.prev.take() { +2309 path.data = prev; +2310 } +2311 path.data.challenges_sent.clear(); +2312 path.data.send_new_challenge = false; +2313 } +2314 PathTimer::PathChallengeLost => { +2315 let Some(path) = self.paths.get_mut(&path_id) else { +2316 continue; +2317 }; +2318 trace!("path challenge deemed lost"); +2319 path.data.send_new_challenge = true; +2320 } +2321 PathTimer::PathOpen => { +2322 let Some(path) = self.paths.get_mut(&path_id) else { +2323 continue; +2324 }; +2325 path.data.challenges_sent.clear(); +2326 path.data.send_new_challenge = false; +2327 self.timers.stop( +2328 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +2329 self.qlog.with_time(now), +2330 ); +2331 debug!("new path validation failed"); +2332 if let Err(err) = self.close_path( +2333 now, +2334 path_id, +2335 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), +2336 ) { +2337 warn!(?err, "failed closing path"); +2338 } +2339 +2340 self.events.push_back(Event::Path(PathEvent::LocallyClosed { +2341 id: path_id, +2342 error: PathError::ValidationFailed, +2343 })); +2344 } +2345 PathTimer::Pacing => trace!("pacing timer expired"), +2346 PathTimer::MaxAckDelay => { +2347 trace!("max ack delay reached"); +2348 // This timer is only armed in the Data space +2349 self.spaces[SpaceId::Data] +2350 .for_path(path_id) +2351 .pending_acks +2352 .on_max_ack_delay_timeout() +2353 } +2354 PathTimer::DiscardPath => { +2355 // The path was abandoned and 3*PTO has expired since. Clean up all +2356 // remaining state and install stateless reset token. +2357 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); +2358 if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) { +2359 let (min_seq, max_seq) = local_cid_state.active_seq(); +2360 for seq in min_seq..=max_seq { +2361 self.endpoint_events.push_back( +2362 EndpointEventInner::RetireConnectionId( +2363 now, path_id, seq, false, +2364 ), +2365 ); +2366 } +2367 } +2368 self.discard_path(path_id, now); +2369 } +2370 } +2371 } +2372 } +2373 } +2374 } +2375 +2376 /// Close a connection immediately +2377 /// +2378 /// This does not ensure delivery of outstanding data. It is the application's responsibility to +2379 /// call this only when all important communications have been completed, e.g. by calling +2380 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding +2381 /// [`StreamEvent::Finished`] event. +2382 /// +2383 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been +2384 /// delivered. There may still be data from the peer that has not been received. 2385 /// -2386 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been -2387 /// delivered. There may still be data from the peer that has not been received. -2388 /// -2389 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished -2390 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { -2391 self.close_inner( -2392 now, -2393 Close::Application(frame::ApplicationClose { error_code, reason }), -2394 ) -2395 } -2396 -2397 /// Close the connection immediately, initiated by an API call. -2398 /// -2399 /// This will not produce a [`ConnectionLost`] event propagated by the -2400 /// [`Connection::poll`] call, because the API call already propagated the error to the -2401 /// user. +2386 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished +2387 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { +2388 self.close_inner( +2389 now, +2390 Close::Application(frame::ApplicationClose { error_code, reason }), +2391 ) +2392 } +2393 +2394 /// Close the connection immediately, initiated by an API call. +2395 /// +2396 /// This will not produce a [`ConnectionLost`] event propagated by the +2397 /// [`Connection::poll`] call, because the API call already propagated the error to the +2398 /// user. +2399 /// +2400 /// Not to be used when entering immediate close due to an internal state change based +2401 /// on an event. See [`State::move_to_closed_local`] for details. 2402 /// -2403 /// Not to be used when entering immediate close due to an internal state change based -2404 /// on an event. See [`State::move_to_closed_local`] for details. -2405 /// -2406 /// This initiates immediate close from -2407 /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed -2408 /// state. -2409 /// -2410 /// [`ConnectionLost`]: crate::Event::ConnectionLost -2411 /// [`Connection::poll`]: super::Connection::poll -2412 fn close_inner(&mut self, now: Instant, reason: Close) { -2413 let was_closed = self.state.is_closed(); -2414 if !was_closed { -2415 self.close_common(); -2416 self.set_close_timer(now); -2417 self.connection_close_pending = true; -2418 self.state.move_to_closed_local(reason); -2419 } -2420 } -2421 -2422 /// Control datagrams -2423 pub fn datagrams(&mut self) -> Datagrams<'_> { -2424 Datagrams { conn: self } -2425 } -2426 -2427 /// Returns connection statistics -2428 pub fn stats(&mut self) -> ConnectionStats { -2429 self.stats.clone() -2430 } -2431 -2432 /// Returns path statistics -2433 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { -2434 let path = self.paths.get(&path_id)?; -2435 let stats = self.path_stats.entry(path_id).or_default(); -2436 stats.rtt = path.data.rtt.get(); -2437 stats.cwnd = path.data.congestion.window(); -2438 stats.current_mtu = path.data.mtud.current_mtu(); -2439 Some(*stats) -2440 } -2441 -2442 /// Ping the remote endpoint -2443 /// -2444 /// Causes an ACK-eliciting packet to be transmitted on the connection. -2445 pub fn ping(&mut self) { -2446 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would -2447 // be nice if we could only send a single packet for this. -2448 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { -2449 path_data.ping_pending = true; -2450 } -2451 } -2452 -2453 /// Ping the remote endpoint over a specific path -2454 /// -2455 /// Causes an ACK-eliciting packet to be transmitted on the path. -2456 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { -2457 let path_data = self.spaces[self.highest_space] -2458 .number_spaces -2459 .get_mut(&path) -2460 .ok_or(ClosedPath { _private: () })?; -2461 path_data.ping_pending = true; -2462 Ok(()) -2463 } -2464 -2465 /// Update traffic keys spontaneously -2466 /// -2467 /// This can be useful for testing key updates, as they otherwise only happen infrequently. -2468 pub fn force_key_update(&mut self) { -2469 if !self.state.is_established() { -2470 debug!("ignoring forced key update in illegal state"); -2471 return; -2472 } -2473 if self.crypto_state.prev_crypto.is_some() { -2474 // We already just updated, or are currently updating, the keys. Concurrent key updates -2475 // are illegal. -2476 debug!("ignoring redundant forced key update"); -2477 return; -2478 } -2479 self.crypto_state.update_keys(None, false); -2480 } -2481 -2482 /// Get a session reference -2483 pub fn crypto_session(&self) -> &dyn crypto::Session { -2484 self.crypto_state.session.as_ref() -2485 } -2486 -2487 /// Whether the connection is in the process of being established -2488 /// -2489 /// If this returns `false`, the connection may be either established or closed, signaled by the -2490 /// emission of a `Connected` or `ConnectionLost` message respectively. -2491 pub fn is_handshaking(&self) -> bool { -2492 self.state.is_handshake() -2493 } -2494 -2495 /// Whether the connection is closed -2496 /// -2497 /// Closed connections cannot transport any further data. A connection becomes closed when -2498 /// either peer application intentionally closes it, or when either transport layer detects an -2499 /// error such as a time-out or certificate validation failure. -2500 /// -2501 /// A `ConnectionLost` event is emitted with details when the connection becomes closed. -2502 pub fn is_closed(&self) -> bool { -2503 self.state.is_closed() -2504 } -2505 -2506 /// Whether there is no longer any need to keep the connection around -2507 /// -2508 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight -2509 /// packets from the peer. All drained connections have been closed. -2510 pub fn is_drained(&self) -> bool { -2511 self.state.is_drained() -2512 } -2513 -2514 /// For clients, if the peer accepted the 0-RTT data packets -2515 /// -2516 /// The value is meaningless until after the handshake completes. -2517 pub fn accepted_0rtt(&self) -> bool { -2518 self.crypto_state.accepted_0rtt -2519 } -2520 -2521 /// Whether 0-RTT is/was possible during the handshake -2522 pub fn has_0rtt(&self) -> bool { -2523 self.crypto_state.zero_rtt_enabled -2524 } -2525 -2526 /// Whether there are any pending retransmits -2527 pub fn has_pending_retransmits(&self) -> bool { -2528 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) -2529 } -2530 -2531 /// Look up whether we're the client or server of this Connection -2532 pub fn side(&self) -> Side { -2533 self.side.side() -2534 } -2535 -2536 /// Get the address observed by the remote over the given path -2537 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { -2538 self.path(path_id) -2539 .map(|path_data| { -2540 path_data -2541 .last_observed_addr_report -2542 .as_ref() -2543 .map(|observed| observed.socket_addr()) -2544 }) -2545 .ok_or(ClosedPath { _private: () }) -2546 } -2547 -2548 /// Current best estimate of this connection's latency (round-trip-time) -2549 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { -2550 self.path(path_id).map(|d| d.rtt.get()) -2551 } -2552 -2553 /// Current state of this connection's congestion controller, for debugging purposes -2554 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { -2555 self.path(path_id).map(|d| d.congestion.as_ref()) -2556 } -2557 -2558 /// Modify the number of remotely initiated streams that may be concurrently open -2559 /// -2560 /// No streams may be opened by the peer unless fewer than `count` are already open. Large -2561 /// `count`s increase both minimum and worst-case memory consumption. -2562 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { -2563 self.streams.set_max_concurrent(dir, count); -2564 // If the limit was reduced, then a flow control update previously deemed insignificant may -2565 // now be significant. -2566 let pending = &mut self.spaces[SpaceId::Data].pending; -2567 self.streams.queue_max_stream_id(pending); -2568 } -2569 -2570 /// Modify the number of open paths allowed when multipath is enabled -2571 /// -2572 /// When reducing the number of concurrent paths this will only affect delaying sending -2573 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To -2574 /// actively reduce paths they must be closed using [`Connection::close_path`], which -2575 /// can also be used to close not-yet-opened paths. -2576 /// -2577 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable -2578 /// multipath and will fail. -2579 pub fn set_max_concurrent_paths( -2580 &mut self, -2581 now: Instant, -2582 count: NonZeroU32, -2583 ) -> Result<(), MultipathNotNegotiated> { -2584 if !self.is_multipath_negotiated() { -2585 return Err(MultipathNotNegotiated { _private: () }); -2586 } -2587 self.max_concurrent_paths = count; -2588 -2589 let in_use_count = self -2590 .local_max_path_id -2591 .next() -2592 .saturating_sub(self.abandoned_paths.len() as u32) -2593 .as_u32(); -2594 let extra_needed = count.get().saturating_sub(in_use_count); -2595 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); -2596 -2597 self.set_max_path_id(now, new_max_path_id); +2403 /// This initiates immediate close from +2404 /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed +2405 /// state. +2406 /// +2407 /// [`ConnectionLost`]: crate::Event::ConnectionLost +2408 /// [`Connection::poll`]: super::Connection::poll +2409 fn close_inner(&mut self, now: Instant, reason: Close) { +2410 let was_closed = self.state.is_closed(); +2411 if !was_closed { +2412 self.close_common(); +2413 self.set_close_timer(now); +2414 self.connection_close_pending = true; +2415 self.state.move_to_closed_local(reason); +2416 } +2417 } +2418 +2419 /// Control datagrams +2420 pub fn datagrams(&mut self) -> Datagrams<'_> { +2421 Datagrams { conn: self } +2422 } +2423 +2424 /// Returns connection statistics +2425 pub fn stats(&mut self) -> ConnectionStats { +2426 self.stats.clone() +2427 } +2428 +2429 /// Returns path statistics +2430 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { +2431 let path = self.paths.get(&path_id)?; +2432 let stats = self.path_stats.entry(path_id).or_default(); +2433 stats.rtt = path.data.rtt.get(); +2434 stats.cwnd = path.data.congestion.window(); +2435 stats.current_mtu = path.data.mtud.current_mtu(); +2436 Some(*stats) +2437 } +2438 +2439 /// Ping the remote endpoint +2440 /// +2441 /// Causes an ACK-eliciting packet to be transmitted on the connection. +2442 pub fn ping(&mut self) { +2443 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would +2444 // be nice if we could only send a single packet for this. +2445 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { +2446 path_data.ping_pending = true; +2447 } +2448 } +2449 +2450 /// Ping the remote endpoint over a specific path +2451 /// +2452 /// Causes an ACK-eliciting packet to be transmitted on the path. +2453 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { +2454 let path_data = self.spaces[self.highest_space] +2455 .number_spaces +2456 .get_mut(&path) +2457 .ok_or(ClosedPath { _private: () })?; +2458 path_data.ping_pending = true; +2459 Ok(()) +2460 } +2461 +2462 /// Update traffic keys spontaneously +2463 /// +2464 /// This can be useful for testing key updates, as they otherwise only happen infrequently. +2465 pub fn force_key_update(&mut self) { +2466 if !self.state.is_established() { +2467 debug!("ignoring forced key update in illegal state"); +2468 return; +2469 } +2470 if self.crypto_state.prev_crypto.is_some() { +2471 // We already just updated, or are currently updating, the keys. Concurrent key updates +2472 // are illegal. +2473 debug!("ignoring redundant forced key update"); +2474 return; +2475 } +2476 self.crypto_state.update_keys(None, false); +2477 } +2478 +2479 /// Get a session reference +2480 pub fn crypto_session(&self) -> &dyn crypto::Session { +2481 self.crypto_state.session.as_ref() +2482 } +2483 +2484 /// Whether the connection is in the process of being established +2485 /// +2486 /// If this returns `false`, the connection may be either established or closed, signaled by the +2487 /// emission of a `Connected` or `ConnectionLost` message respectively. +2488 pub fn is_handshaking(&self) -> bool { +2489 self.state.is_handshake() +2490 } +2491 +2492 /// Whether the connection is closed +2493 /// +2494 /// Closed connections cannot transport any further data. A connection becomes closed when +2495 /// either peer application intentionally closes it, or when either transport layer detects an +2496 /// error such as a time-out or certificate validation failure. +2497 /// +2498 /// A `ConnectionLost` event is emitted with details when the connection becomes closed. +2499 pub fn is_closed(&self) -> bool { +2500 self.state.is_closed() +2501 } +2502 +2503 /// Whether there is no longer any need to keep the connection around +2504 /// +2505 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight +2506 /// packets from the peer. All drained connections have been closed. +2507 pub fn is_drained(&self) -> bool { +2508 self.state.is_drained() +2509 } +2510 +2511 /// For clients, if the peer accepted the 0-RTT data packets +2512 /// +2513 /// The value is meaningless until after the handshake completes. +2514 pub fn accepted_0rtt(&self) -> bool { +2515 self.crypto_state.accepted_0rtt +2516 } +2517 +2518 /// Whether 0-RTT is/was possible during the handshake +2519 pub fn has_0rtt(&self) -> bool { +2520 self.crypto_state.zero_rtt_enabled +2521 } +2522 +2523 /// Whether there are any pending retransmits +2524 pub fn has_pending_retransmits(&self) -> bool { +2525 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) +2526 } +2527 +2528 /// Look up whether we're the client or server of this Connection +2529 pub fn side(&self) -> Side { +2530 self.side.side() +2531 } +2532 +2533 /// Get the address observed by the remote over the given path +2534 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { +2535 self.path(path_id) +2536 .map(|path_data| { +2537 path_data +2538 .last_observed_addr_report +2539 .as_ref() +2540 .map(|observed| observed.socket_addr()) +2541 }) +2542 .ok_or(ClosedPath { _private: () }) +2543 } +2544 +2545 /// Current best estimate of this connection's latency (round-trip-time) +2546 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { +2547 self.path(path_id).map(|d| d.rtt.get()) +2548 } +2549 +2550 /// Current state of this connection's congestion controller, for debugging purposes +2551 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { +2552 self.path(path_id).map(|d| d.congestion.as_ref()) +2553 } +2554 +2555 /// Modify the number of remotely initiated streams that may be concurrently open +2556 /// +2557 /// No streams may be opened by the peer unless fewer than `count` are already open. Large +2558 /// `count`s increase both minimum and worst-case memory consumption. +2559 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { +2560 self.streams.set_max_concurrent(dir, count); +2561 // If the limit was reduced, then a flow control update previously deemed insignificant may +2562 // now be significant. +2563 let pending = &mut self.spaces[SpaceId::Data].pending; +2564 self.streams.queue_max_stream_id(pending); +2565 } +2566 +2567 /// Modify the number of open paths allowed when multipath is enabled +2568 /// +2569 /// When reducing the number of concurrent paths this will only affect delaying sending +2570 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To +2571 /// actively reduce paths they must be closed using [`Connection::close_path`], which +2572 /// can also be used to close not-yet-opened paths. +2573 /// +2574 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable +2575 /// multipath and will fail. +2576 pub fn set_max_concurrent_paths( +2577 &mut self, +2578 now: Instant, +2579 count: NonZeroU32, +2580 ) -> Result<(), MultipathNotNegotiated> { +2581 if !self.is_multipath_negotiated() { +2582 return Err(MultipathNotNegotiated { _private: () }); +2583 } +2584 self.max_concurrent_paths = count; +2585 +2586 let in_use_count = self +2587 .local_max_path_id +2588 .next() +2589 .saturating_sub(self.abandoned_paths.len() as u32) +2590 .as_u32(); +2591 let extra_needed = count.get().saturating_sub(in_use_count); +2592 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); +2593 +2594 self.set_max_path_id(now, new_max_path_id); +2595 +2596 Ok(()) +2597 } 2598 -2599 Ok(()) -2600 } -2601 -2602 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths -2603 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { -2604 if max_path_id <= self.local_max_path_id { -2605 return; -2606 } +2599 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths +2600 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { +2601 if max_path_id <= self.local_max_path_id { +2602 return; +2603 } +2604 +2605 self.local_max_path_id = max_path_id; +2606 self.spaces[SpaceId::Data].pending.max_path_id = true; 2607 -2608 self.local_max_path_id = max_path_id; -2609 self.spaces[SpaceId::Data].pending.max_path_id = true; +2608 self.issue_first_path_cids(now); +2609 } 2610 -2611 self.issue_first_path_cids(now); -2612 } -2613 -2614 /// Current number of remotely initiated streams that may be concurrently open -2615 /// -2616 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), -2617 /// it will not change immediately, even if fewer streams are open. Instead, it will -2618 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. -2619 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { -2620 self.streams.max_concurrent(dir) -2621 } -2622 -2623 /// See [`TransportConfig::send_window()`] -2624 pub fn set_send_window(&mut self, send_window: u64) { -2625 self.streams.set_send_window(send_window); -2626 } -2627 -2628 /// See [`TransportConfig::receive_window()`] -2629 pub fn set_receive_window(&mut self, receive_window: VarInt) { -2630 if self.streams.set_receive_window(receive_window) { -2631 self.spaces[SpaceId::Data].pending.max_data = true; -2632 } -2633 } -2634 -2635 /// Whether the Multipath for QUIC extension is enabled. -2636 /// -2637 /// Multipath is only enabled after the handshake is completed and if it was enabled by both -2638 /// peers. -2639 pub fn is_multipath_negotiated(&self) -> bool { -2640 !self.is_handshaking() -2641 && self.config.max_concurrent_multipath_paths.is_some() -2642 && self.peer_params.initial_max_path_id.is_some() -2643 } -2644 -2645 fn on_ack_received( -2646 &mut self, -2647 now: Instant, -2648 space: SpaceId, -2649 ack: frame::Ack, -2650 ) -> Result<(), TransportError> { -2651 // All ACKs are referencing path 0 -2652 let path = PathId::ZERO; -2653 self.inner_on_ack_received(now, space, path, ack) -2654 } -2655 -2656 fn on_path_ack_received( -2657 &mut self, -2658 now: Instant, -2659 space: SpaceId, -2660 path_ack: frame::PathAck, -2661 ) -> Result<(), TransportError> { -2662 let (ack, path) = path_ack.into_ack(); -2663 self.inner_on_ack_received(now, space, path, ack) -2664 } -2665 -2666 /// Handles an ACK frame acknowledging packets sent on *path*. -2667 fn inner_on_ack_received( -2668 &mut self, -2669 now: Instant, -2670 space: SpaceId, -2671 path: PathId, -2672 ack: frame::Ack, -2673 ) -> Result<(), TransportError> { -2674 if self.abandoned_paths.contains(&path) { -2675 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3 -2676 // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4. -2677 trace!("silently ignoring PATH_ACK on abandoned path"); -2678 return Ok(()); +2611 /// Current number of remotely initiated streams that may be concurrently open +2612 /// +2613 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), +2614 /// it will not change immediately, even if fewer streams are open. Instead, it will +2615 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. +2616 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { +2617 self.streams.max_concurrent(dir) +2618 } +2619 +2620 /// See [`TransportConfig::send_window()`] +2621 pub fn set_send_window(&mut self, send_window: u64) { +2622 self.streams.set_send_window(send_window); +2623 } +2624 +2625 /// See [`TransportConfig::receive_window()`] +2626 pub fn set_receive_window(&mut self, receive_window: VarInt) { +2627 if self.streams.set_receive_window(receive_window) { +2628 self.spaces[SpaceId::Data].pending.max_data = true; +2629 } +2630 } +2631 +2632 /// Whether the Multipath for QUIC extension is enabled. +2633 /// +2634 /// Multipath is only enabled after the handshake is completed and if it was enabled by both +2635 /// peers. +2636 pub fn is_multipath_negotiated(&self) -> bool { +2637 !self.is_handshaking() +2638 && self.config.max_concurrent_multipath_paths.is_some() +2639 && self.peer_params.initial_max_path_id.is_some() +2640 } +2641 +2642 fn on_ack_received( +2643 &mut self, +2644 now: Instant, +2645 space: SpaceId, +2646 ack: frame::Ack, +2647 ) -> Result<(), TransportError> { +2648 // All ACKs are referencing path 0 +2649 let path = PathId::ZERO; +2650 self.inner_on_ack_received(now, space, path, ack) +2651 } +2652 +2653 fn on_path_ack_received( +2654 &mut self, +2655 now: Instant, +2656 space: SpaceId, +2657 path_ack: frame::PathAck, +2658 ) -> Result<(), TransportError> { +2659 let (ack, path) = path_ack.into_ack(); +2660 self.inner_on_ack_received(now, space, path, ack) +2661 } +2662 +2663 /// Handles an ACK frame acknowledging packets sent on *path*. +2664 fn inner_on_ack_received( +2665 &mut self, +2666 now: Instant, +2667 space: SpaceId, +2668 path: PathId, +2669 ack: frame::Ack, +2670 ) -> Result<(), TransportError> { +2671 if self.abandoned_paths.contains(&path) { +2672 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3 +2673 // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4. +2674 trace!("silently ignoring PATH_ACK on abandoned path"); +2675 return Ok(()); +2676 } +2677 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { +2678 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); 2679 } -2680 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { -2681 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); -2682 } -2683 let new_largest = { -2684 let space = &mut self.spaces[space].for_path(path); -2685 if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) { -2686 space.largest_acked_packet = Some(ack.largest); -2687 if let Some(info) = space.sent_packets.get(ack.largest) { -2688 // This should always succeed, but a misbehaving peer might ACK a packet we -2689 // haven't sent. At worst, that will result in us spuriously reducing the -2690 // congestion window. -2691 space.largest_acked_packet_sent = info.time_sent; -2692 } -2693 true -2694 } else { -2695 false -2696 } -2697 }; -2698 -2699 if self.detect_spurious_loss(&ack, space, path) { -2700 self.path_data_mut(path) -2701 .congestion -2702 .on_spurious_congestion_event(); -2703 } -2704 -2705 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. -2706 let mut newly_acked = ArrayRangeSet::new(); -2707 for range in ack.iter() { -2708 self.spaces[space].for_path(path).check_ack(range.clone())?; -2709 for (pn, _) in self.spaces[space] -2710 .for_path(path) -2711 .sent_packets -2712 .iter_range(range) -2713 { -2714 newly_acked.insert_one(pn); -2715 } -2716 } -2717 -2718 if newly_acked.is_empty() { -2719 return Ok(()); -2720 } -2721 -2722 let mut ack_eliciting_acked = false; -2723 for packet in newly_acked.elts() { -2724 if let Some(info) = self.spaces[space].for_path(path).take(packet) { -2725 for (acked_path_id, acked_pn) in info.largest_acked.iter() { -2726 // Assume ACKs for all packets below the largest acknowledged in -2727 // `packet` have been received. This can cause the peer to spuriously -2728 // retransmit if some of our earlier ACKs were lost, but allows for -2729 // simpler state tracking. See discussion at -2730 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking -2731 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { -2732 pns.pending_acks.subtract_below(*acked_pn); -2733 } -2734 } -2735 ack_eliciting_acked |= info.ack_eliciting; -2736 -2737 // Notify MTU discovery that a packet was acked, because it might be an MTU probe -2738 let path_data = self.path_data_mut(path); -2739 let mtu_updated = path_data.mtud.on_acked(space.kind(), packet, info.size); -2740 if mtu_updated { -2741 path_data -2742 .congestion -2743 .on_mtu_update(path_data.mtud.current_mtu()); -2744 } +2680 let new_largest = { +2681 let space = &mut self.spaces[space].for_path(path); +2682 if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) { +2683 space.largest_acked_packet = Some(ack.largest); +2684 if let Some(info) = space.sent_packets.get(ack.largest) { +2685 // This should always succeed, but a misbehaving peer might ACK a packet we +2686 // haven't sent. At worst, that will result in us spuriously reducing the +2687 // congestion window. +2688 space.largest_acked_packet_sent = info.time_sent; +2689 } +2690 true +2691 } else { +2692 false +2693 } +2694 }; +2695 +2696 if self.detect_spurious_loss(&ack, space, path) { +2697 self.path_data_mut(path) +2698 .congestion +2699 .on_spurious_congestion_event(); +2700 } +2701 +2702 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. +2703 let mut newly_acked = ArrayRangeSet::new(); +2704 for range in ack.iter() { +2705 self.spaces[space].for_path(path).check_ack(range.clone())?; +2706 for (pn, _) in self.spaces[space] +2707 .for_path(path) +2708 .sent_packets +2709 .iter_range(range) +2710 { +2711 newly_acked.insert_one(pn); +2712 } +2713 } +2714 +2715 if newly_acked.is_empty() { +2716 return Ok(()); +2717 } +2718 +2719 let mut ack_eliciting_acked = false; +2720 for packet in newly_acked.elts() { +2721 if let Some(info) = self.spaces[space].for_path(path).take(packet) { +2722 for (acked_path_id, acked_pn) in info.largest_acked.iter() { +2723 // Assume ACKs for all packets below the largest acknowledged in +2724 // `packet` have been received. This can cause the peer to spuriously +2725 // retransmit if some of our earlier ACKs were lost, but allows for +2726 // simpler state tracking. See discussion at +2727 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking +2728 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { +2729 pns.pending_acks.subtract_below(*acked_pn); +2730 } +2731 } +2732 ack_eliciting_acked |= info.ack_eliciting; +2733 +2734 // Notify MTU discovery that a packet was acked, because it might be an MTU probe +2735 let path_data = self.path_data_mut(path); +2736 let mtu_updated = path_data.mtud.on_acked(space.kind(), packet, info.size); +2737 if mtu_updated { +2738 path_data +2739 .congestion +2740 .on_mtu_update(path_data.mtud.current_mtu()); +2741 } +2742 +2743 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame +2744 self.ack_frequency.on_acked(path, packet); 2745 -2746 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame -2747 self.ack_frequency.on_acked(path, packet); -2748 -2749 self.on_packet_acked(now, path, info); -2750 } -2751 } -2752 -2753 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet; -2754 let app_limited = self.app_limited; -2755 let path_data = self.path_data_mut(path); -2756 let in_flight = path_data.in_flight.bytes; -2757 -2758 path_data -2759 .congestion -2760 .on_end_acks(now, in_flight, app_limited, largest_ackd); -2761 -2762 if new_largest && ack_eliciting_acked { -2763 let ack_delay = if space != SpaceId::Data { -2764 Duration::from_micros(0) -2765 } else { -2766 cmp::min( -2767 self.ack_frequency.peer_max_ack_delay, -2768 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), -2769 ) -2770 }; -2771 let rtt = now.saturating_duration_since( -2772 self.spaces[space].for_path(path).largest_acked_packet_sent, -2773 ); -2774 -2775 let next_pn = self.spaces[space].for_path(path).next_packet_number; -2776 let path_data = self.path_data_mut(path); -2777 // TODO(@divma): should be a method of path, should be contained in a single place -2778 path_data.rtt.update(ack_delay, rtt); -2779 if path_data.first_packet_after_rtt_sample.is_none() { -2780 path_data.first_packet_after_rtt_sample = Some((space.kind(), next_pn)); -2781 } -2782 } +2746 self.on_packet_acked(now, path, info); +2747 } +2748 } +2749 +2750 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet; +2751 let app_limited = self.app_limited; +2752 let path_data = self.path_data_mut(path); +2753 let in_flight = path_data.in_flight.bytes; +2754 +2755 path_data +2756 .congestion +2757 .on_end_acks(now, in_flight, app_limited, largest_ackd); +2758 +2759 if new_largest && ack_eliciting_acked { +2760 let ack_delay = if space != SpaceId::Data { +2761 Duration::from_micros(0) +2762 } else { +2763 cmp::min( +2764 self.ack_frequency.peer_max_ack_delay, +2765 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), +2766 ) +2767 }; +2768 let rtt = now.saturating_duration_since( +2769 self.spaces[space].for_path(path).largest_acked_packet_sent, +2770 ); +2771 +2772 let next_pn = self.spaces[space].for_path(path).next_packet_number; +2773 let path_data = self.path_data_mut(path); +2774 // TODO(@divma): should be a method of path, should be contained in a single place +2775 path_data.rtt.update(ack_delay, rtt); +2776 if path_data.first_packet_after_rtt_sample.is_none() { +2777 path_data.first_packet_after_rtt_sample = Some((space.kind(), next_pn)); +2778 } +2779 } +2780 +2781 // Must be called before crypto/pto_count are clobbered +2782 self.detect_lost_packets(now, space, path, true); 2783 -2784 // Must be called before crypto/pto_count are clobbered -2785 self.detect_lost_packets(now, space, path, true); -2786 -2787 if self.peer_completed_address_validation(path) { -2788 self.path_data_mut(path).pto_count = 0; -2789 } -2790 -2791 // Explicit congestion notification -2792 // TODO(@divma): this code is a good example of logic that should be contained in a single -2793 // place but it's split between the path data and the packet number space data, we should -2794 // find a way to make this work without two lookups -2795 if self.path_data(path).sending_ecn { -2796 if let Some(ecn) = ack.ecn { -2797 // We only examine ECN counters from ACKs that we are certain we received in transmit -2798 // order, allowing us to compute an increase in ECN counts to compare against the number -2799 // of newly acked packets that remains well-defined in the presence of arbitrary packet -2800 // reordering. -2801 if new_largest { -2802 let sent = self.spaces[space].for_path(path).largest_acked_packet_sent; -2803 self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent); -2804 } -2805 } else { -2806 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. -2807 debug!("ECN not acknowledged by peer"); -2808 self.path_data_mut(path).sending_ecn = false; -2809 } -2810 } -2811 -2812 self.set_loss_detection_timer(now, path); -2813 Ok(()) -2814 } +2784 if self.peer_completed_address_validation(path) { +2785 self.path_data_mut(path).pto_count = 0; +2786 } +2787 +2788 // Explicit congestion notification +2789 // TODO(@divma): this code is a good example of logic that should be contained in a single +2790 // place but it's split between the path data and the packet number space data, we should +2791 // find a way to make this work without two lookups +2792 if self.path_data(path).sending_ecn { +2793 if let Some(ecn) = ack.ecn { +2794 // We only examine ECN counters from ACKs that we are certain we received in transmit +2795 // order, allowing us to compute an increase in ECN counts to compare against the number +2796 // of newly acked packets that remains well-defined in the presence of arbitrary packet +2797 // reordering. +2798 if new_largest { +2799 let sent = self.spaces[space].for_path(path).largest_acked_packet_sent; +2800 self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent); +2801 } +2802 } else { +2803 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. +2804 debug!("ECN not acknowledged by peer"); +2805 self.path_data_mut(path).sending_ecn = false; +2806 } +2807 } +2808 +2809 self.set_loss_detection_timer(now, path); +2810 Ok(()) +2811 } +2812 +2813 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { +2814 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; 2815 -2816 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { -2817 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -2818 -2819 if lost_packets.is_empty() { -2820 return false; -2821 } -2822 -2823 for range in ack.iter() { -2824 let spurious_losses: Vec<u64> = lost_packets -2825 .iter_range(range.clone()) -2826 .map(|(pn, _info)| pn) -2827 .collect(); -2828 -2829 for pn in spurious_losses { -2830 lost_packets.remove(pn); -2831 } -2832 } -2833 -2834 // If this ACK frame acknowledged all deemed lost packets, -2835 // then we have raised a spurious congestion event in the past. -2836 // We cannot conclude when there are remaining packets, -2837 // but future ACK frames might indicate a spurious loss detection. -2838 lost_packets.is_empty() -2839 } -2840 -2841 /// Drain lost packets that we reasonably think will never arrive -2842 /// -2843 /// The current criterion is copied from `msquic`: -2844 /// discard packets that were sent earlier than 2 probe timeouts ago. -2845 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { -2846 let two_pto = 2 * self.path_data(path).rtt.pto_base(); -2847 -2848 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -2849 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); -2850 } -2851 -2852 /// Process a new ECN block from an in-order ACK -2853 fn process_ecn( -2854 &mut self, -2855 now: Instant, -2856 space: SpaceId, -2857 path: PathId, -2858 newly_acked: u64, -2859 ecn: frame::EcnCounts, -2860 largest_sent_time: Instant, -2861 ) { -2862 match self.spaces[space] -2863 .for_path(path) -2864 .detect_ecn(newly_acked, ecn) -2865 { -2866 Err(e) => { -2867 debug!("halting ECN due to verification failure: {}", e); -2868 -2869 self.path_data_mut(path).sending_ecn = false; -2870 // Wipe out the existing value because it might be garbage and could interfere with -2871 // future attempts to use ECN on new paths. -2872 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; -2873 } -2874 Ok(false) => {} -2875 Ok(true) => { -2876 self.path_stats.entry(path).or_default().congestion_events += 1; -2877 self.path_data_mut(path).congestion.on_congestion_event( -2878 now, -2879 largest_sent_time, -2880 false, -2881 true, -2882 0, -2883 ); -2884 } -2885 } -2886 } -2887 -2888 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from -2889 // high-latency handshakes -2890 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) { -2891 self.paths -2892 .get_mut(&path_id) -2893 .expect("known path") -2894 .remove_in_flight(&info); -2895 let app_limited = self.app_limited; -2896 let path = self.path_data_mut(path_id); -2897 if info.ack_eliciting && !path.is_validating_path() { -2898 // Only pass ACKs to the congestion controller if we are not validating the current -2899 // path, so as to ignore any ACKs from older paths still coming in. -2900 let rtt = path.rtt; -2901 path.congestion -2902 .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt); -2903 } -2904 -2905 // Update state for confirmed delivery of frames -2906 if let Some(retransmits) = info.retransmits.get() { -2907 for (id, _) in retransmits.reset_stream.iter() { -2908 self.streams.reset_acked(*id); -2909 } -2910 } -2911 -2912 for frame in info.stream_frames { -2913 self.streams.received_ack_of(frame); -2914 } -2915 } -2916 -2917 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceKind) { -2918 let start = if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) { -2919 now -2920 } else { -2921 self.crypto_state -2922 .prev_crypto +2816 if lost_packets.is_empty() { +2817 return false; +2818 } +2819 +2820 for range in ack.iter() { +2821 let spurious_losses: Vec<u64> = lost_packets +2822 .iter_range(range.clone()) +2823 .map(|(pn, _info)| pn) +2824 .collect(); +2825 +2826 for pn in spurious_losses { +2827 lost_packets.remove(pn); +2828 } +2829 } +2830 +2831 // If this ACK frame acknowledged all deemed lost packets, +2832 // then we have raised a spurious congestion event in the past. +2833 // We cannot conclude when there are remaining packets, +2834 // but future ACK frames might indicate a spurious loss detection. +2835 lost_packets.is_empty() +2836 } +2837 +2838 /// Drain lost packets that we reasonably think will never arrive +2839 /// +2840 /// The current criterion is copied from `msquic`: +2841 /// discard packets that were sent earlier than 2 probe timeouts ago. +2842 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { +2843 let two_pto = 2 * self.path_data(path).rtt.pto_base(); +2844 +2845 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; +2846 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); +2847 } +2848 +2849 /// Process a new ECN block from an in-order ACK +2850 fn process_ecn( +2851 &mut self, +2852 now: Instant, +2853 space: SpaceId, +2854 path: PathId, +2855 newly_acked: u64, +2856 ecn: frame::EcnCounts, +2857 largest_sent_time: Instant, +2858 ) { +2859 match self.spaces[space] +2860 .for_path(path) +2861 .detect_ecn(newly_acked, ecn) +2862 { +2863 Err(e) => { +2864 debug!("halting ECN due to verification failure: {}", e); +2865 +2866 self.path_data_mut(path).sending_ecn = false; +2867 // Wipe out the existing value because it might be garbage and could interfere with +2868 // future attempts to use ECN on new paths. +2869 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; +2870 } +2871 Ok(false) => {} +2872 Ok(true) => { +2873 self.path_stats.entry(path).or_default().congestion_events += 1; +2874 self.path_data_mut(path).congestion.on_congestion_event( +2875 now, +2876 largest_sent_time, +2877 false, +2878 true, +2879 0, +2880 ); +2881 } +2882 } +2883 } +2884 +2885 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from +2886 // high-latency handshakes +2887 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) { +2888 self.paths +2889 .get_mut(&path_id) +2890 .expect("known path") +2891 .remove_in_flight(&info); +2892 let app_limited = self.app_limited; +2893 let path = self.path_data_mut(path_id); +2894 if info.ack_eliciting && !path.is_validating_path() { +2895 // Only pass ACKs to the congestion controller if we are not validating the current +2896 // path, so as to ignore any ACKs from older paths still coming in. +2897 let rtt = path.rtt; +2898 path.congestion +2899 .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt); +2900 } +2901 +2902 // Update state for confirmed delivery of frames +2903 if let Some(retransmits) = info.retransmits.get() { +2904 for (id, _) in retransmits.reset_stream.iter() { +2905 self.streams.reset_acked(*id); +2906 } +2907 } +2908 +2909 for frame in info.stream_frames { +2910 self.streams.received_ack_of(frame); +2911 } +2912 } +2913 +2914 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceKind) { +2915 let start = if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) { +2916 now +2917 } else { +2918 self.crypto_state +2919 .prev_crypto +2920 .as_ref() +2921 .expect("no previous keys") +2922 .end_packet 2923 .as_ref() -2924 .expect("no previous keys") -2925 .end_packet -2926 .as_ref() -2927 .expect("update not acknowledged yet") -2928 .1 -2929 }; -2930 -2931 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths. -2932 self.timers.set( -2933 Timer::Conn(ConnTimer::KeyDiscard), -2934 start + self.max_pto_for_space(space) * 3, -2935 self.qlog.with_time(now), -2936 ); -2937 } -2938 -2939 /// Handle a [`PathTimer::LossDetection`] timeout. -2940 /// -2941 /// This timer expires for two reasons: -2942 /// - An ACK-eliciting packet we sent should be considered lost. -2943 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. -2944 /// -2945 /// The former needs us to schedule re-transmission of the lost data. -2946 /// -2947 /// The latter means we have not received an ACK for an ack-eliciting packet we sent -2948 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting -2949 /// packet, to try and elicit new acknowledgements. These new acknowledgements will -2950 /// indicate whether the previously sent packets were lost or not. -2951 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { -2952 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { -2953 // Time threshold loss Detection -2954 self.detect_lost_packets(now, pn_space, path_id, false); -2955 self.set_loss_detection_timer(now, path_id); -2956 return; -2957 } -2958 -2959 let (_, space) = match self.pto_time_and_space(now, path_id) { -2960 Some(x) => x, -2961 None => { -2962 error!(%path_id, "PTO expired while unset"); -2963 return; -2964 } -2965 }; -2966 trace!( -2967 in_flight = self.path_data(path_id).in_flight.bytes, -2968 count = self.path_data(path_id).pto_count, -2969 ?space, -2970 %path_id, -2971 "PTO fired" -2972 ); -2973 -2974 let count = match self.path_data(path_id).in_flight.ack_eliciting { -2975 // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification -2976 // deadlock preventions -2977 0 => { -2978 debug_assert!(!self.peer_completed_address_validation(path_id)); -2979 1 -2980 } -2981 // Conventional loss probe -2982 _ => 2, -2983 }; -2984 let pns = self.spaces[space].for_path(path_id); -2985 pns.loss_probes = pns.loss_probes.saturating_add(count); -2986 let path_data = self.path_data_mut(path_id); -2987 path_data.pto_count = path_data.pto_count.saturating_add(1); -2988 self.set_loss_detection_timer(now, path_id); -2989 } -2990 -2991 /// Detect any lost packets -2992 /// -2993 /// There are two cases in which we detects lost packets: -2994 /// -2995 /// - We received an ACK packet. -2996 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet -2997 /// that was followed by an acknowledged packet. The loss timer for this -2998 /// un-acknowledged packet expired and we need to detect that packet as lost. -2999 /// -3000 /// Packets are lost if they are both (See RFC9002 §6.1): -3001 /// -3002 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. -3003 /// - Old enough by either: -3004 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last -3005 /// acknowledged packet. -3006 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. -3007 fn detect_lost_packets( -3008 &mut self, -3009 now: Instant, -3010 pn_space: SpaceId, -3011 path_id: PathId, -3012 due_to_ack: bool, -3013 ) { -3014 let mut lost_packets = Vec::<u64>::new(); -3015 let mut lost_mtu_probe = None; -3016 let mut in_persistent_congestion = false; -3017 let mut size_of_lost_packets = 0u64; -3018 self.spaces[pn_space].for_path(path_id).loss_time = None; -3019 -3020 // Find all the lost packets, populating all variables initialised above. -3021 -3022 let path = self.path_data(path_id); -3023 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -3024 let loss_delay = path -3025 .rtt -3026 .conservative() -3027 .mul_f32(self.config.time_threshold) -3028 .max(TIMER_GRANULARITY); -3029 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; -3030 -3031 let largest_acked_packet = self.spaces[pn_space] -3032 .for_path(path_id) -3033 .largest_acked_packet -3034 .expect("detect_lost_packets only to be called if path received at least one ACK"); -3035 let packet_threshold = self.config.packet_threshold as u64; -3036 -3037 // InPersistentCongestion: Determine if all packets in the time period before the newest -3038 // lost packet, including the edges, are marked lost. PTO computation must always -3039 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). -3040 let congestion_period = self -3041 .pto(SpaceKind::Data, path_id) -3042 .saturating_mul(self.config.persistent_congestion_threshold); -3043 let mut persistent_congestion_start: Option<Instant> = None; -3044 let mut prev_packet = None; -3045 let space = self.spaces[pn_space].for_path(path_id); -3046 -3047 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) { -3048 if prev_packet != Some(packet.wrapping_sub(1)) { -3049 // An intervening packet was acknowledged -3050 persistent_congestion_start = None; -3051 } -3052 -3053 // Packets sent before now - loss_delay are deemed lost. -3054 // However, we avoid subtraction as it can panic and there's no -3055 // saturating equivalent of this subtraction operation with a Duration. -3056 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; -3057 if packet_too_old || largest_acked_packet >= packet + packet_threshold { -3058 // The packet should be declared lost. -3059 if Some(packet) == in_flight_mtu_probe { -3060 // Lost MTU probes are not included in `lost_packets`, because they -3061 // should not trigger a congestion control response -3062 lost_mtu_probe = in_flight_mtu_probe; -3063 } else { -3064 lost_packets.push(packet); -3065 size_of_lost_packets += info.size as u64; -3066 if info.ack_eliciting && due_to_ack { -3067 match persistent_congestion_start { -3068 // Two ACK-eliciting packets lost more than -3069 // congestion_period apart, with no ACKed packets in between -3070 Some(start) if info.time_sent - start > congestion_period => { -3071 in_persistent_congestion = true; -3072 } -3073 // Persistent congestion must start after the first RTT sample -3074 None if first_packet_after_rtt_sample -3075 .is_some_and(|x| x < (pn_space.kind(), packet)) => -3076 { -3077 persistent_congestion_start = Some(info.time_sent); -3078 } -3079 _ => {} -3080 } -3081 } -3082 } -3083 } else { -3084 // The packet should not yet be declared lost. -3085 if space.loss_time.is_none() { -3086 // Since we iterate in order the lowest packet number's loss time will -3087 // always be the earliest. -3088 space.loss_time = Some(info.time_sent + loss_delay); -3089 } -3090 persistent_congestion_start = None; -3091 } +2924 .expect("update not acknowledged yet") +2925 .1 +2926 }; +2927 +2928 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths. +2929 self.timers.set( +2930 Timer::Conn(ConnTimer::KeyDiscard), +2931 start + self.max_pto_for_space(space) * 3, +2932 self.qlog.with_time(now), +2933 ); +2934 } +2935 +2936 /// Handle a [`PathTimer::LossDetection`] timeout. +2937 /// +2938 /// This timer expires for two reasons: +2939 /// - An ACK-eliciting packet we sent should be considered lost. +2940 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. +2941 /// +2942 /// The former needs us to schedule re-transmission of the lost data. +2943 /// +2944 /// The latter means we have not received an ACK for an ack-eliciting packet we sent +2945 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting +2946 /// packet, to try and elicit new acknowledgements. These new acknowledgements will +2947 /// indicate whether the previously sent packets were lost or not. +2948 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { +2949 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { +2950 // Time threshold loss Detection +2951 self.detect_lost_packets(now, pn_space, path_id, false); +2952 self.set_loss_detection_timer(now, path_id); +2953 return; +2954 } +2955 +2956 let (_, space) = match self.pto_time_and_space(now, path_id) { +2957 Some(x) => x, +2958 None => { +2959 error!(%path_id, "PTO expired while unset"); +2960 return; +2961 } +2962 }; +2963 trace!( +2964 in_flight = self.path_data(path_id).in_flight.bytes, +2965 count = self.path_data(path_id).pto_count, +2966 ?space, +2967 %path_id, +2968 "PTO fired" +2969 ); +2970 +2971 let count = match self.path_data(path_id).in_flight.ack_eliciting { +2972 // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification +2973 // deadlock preventions +2974 0 => { +2975 debug_assert!(!self.peer_completed_address_validation(path_id)); +2976 1 +2977 } +2978 // Conventional loss probe +2979 _ => 2, +2980 }; +2981 let pns = self.spaces[space].for_path(path_id); +2982 pns.loss_probes = pns.loss_probes.saturating_add(count); +2983 let path_data = self.path_data_mut(path_id); +2984 path_data.pto_count = path_data.pto_count.saturating_add(1); +2985 self.set_loss_detection_timer(now, path_id); +2986 } +2987 +2988 /// Detect any lost packets +2989 /// +2990 /// There are two cases in which we detects lost packets: +2991 /// +2992 /// - We received an ACK packet. +2993 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet +2994 /// that was followed by an acknowledged packet. The loss timer for this +2995 /// un-acknowledged packet expired and we need to detect that packet as lost. +2996 /// +2997 /// Packets are lost if they are both (See RFC9002 §6.1): +2998 /// +2999 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. +3000 /// - Old enough by either: +3001 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last +3002 /// acknowledged packet. +3003 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. +3004 fn detect_lost_packets( +3005 &mut self, +3006 now: Instant, +3007 pn_space: SpaceId, +3008 path_id: PathId, +3009 due_to_ack: bool, +3010 ) { +3011 let mut lost_packets = Vec::<u64>::new(); +3012 let mut lost_mtu_probe = None; +3013 let mut in_persistent_congestion = false; +3014 let mut size_of_lost_packets = 0u64; +3015 self.spaces[pn_space].for_path(path_id).loss_time = None; +3016 +3017 // Find all the lost packets, populating all variables initialised above. +3018 +3019 let path = self.path_data(path_id); +3020 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); +3021 let loss_delay = path +3022 .rtt +3023 .conservative() +3024 .mul_f32(self.config.time_threshold) +3025 .max(TIMER_GRANULARITY); +3026 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; +3027 +3028 let largest_acked_packet = self.spaces[pn_space] +3029 .for_path(path_id) +3030 .largest_acked_packet +3031 .expect("detect_lost_packets only to be called if path received at least one ACK"); +3032 let packet_threshold = self.config.packet_threshold as u64; +3033 +3034 // InPersistentCongestion: Determine if all packets in the time period before the newest +3035 // lost packet, including the edges, are marked lost. PTO computation must always +3036 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). +3037 let congestion_period = self +3038 .pto(SpaceKind::Data, path_id) +3039 .saturating_mul(self.config.persistent_congestion_threshold); +3040 let mut persistent_congestion_start: Option<Instant> = None; +3041 let mut prev_packet = None; +3042 let space = self.spaces[pn_space].for_path(path_id); +3043 +3044 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) { +3045 if prev_packet != Some(packet.wrapping_sub(1)) { +3046 // An intervening packet was acknowledged +3047 persistent_congestion_start = None; +3048 } +3049 +3050 // Packets sent before now - loss_delay are deemed lost. +3051 // However, we avoid subtraction as it can panic and there's no +3052 // saturating equivalent of this subtraction operation with a Duration. +3053 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; +3054 if packet_too_old || largest_acked_packet >= packet + packet_threshold { +3055 // The packet should be declared lost. +3056 if Some(packet) == in_flight_mtu_probe { +3057 // Lost MTU probes are not included in `lost_packets`, because they +3058 // should not trigger a congestion control response +3059 lost_mtu_probe = in_flight_mtu_probe; +3060 } else { +3061 lost_packets.push(packet); +3062 size_of_lost_packets += info.size as u64; +3063 if info.ack_eliciting && due_to_ack { +3064 match persistent_congestion_start { +3065 // Two ACK-eliciting packets lost more than +3066 // congestion_period apart, with no ACKed packets in between +3067 Some(start) if info.time_sent - start > congestion_period => { +3068 in_persistent_congestion = true; +3069 } +3070 // Persistent congestion must start after the first RTT sample +3071 None if first_packet_after_rtt_sample +3072 .is_some_and(|x| x < (pn_space.kind(), packet)) => +3073 { +3074 persistent_congestion_start = Some(info.time_sent); +3075 } +3076 _ => {} +3077 } +3078 } +3079 } +3080 } else { +3081 // The packet should not yet be declared lost. +3082 if space.loss_time.is_none() { +3083 // Since we iterate in order the lowest packet number's loss time will +3084 // always be the earliest. +3085 space.loss_time = Some(info.time_sent + loss_delay); +3086 } +3087 persistent_congestion_start = None; +3088 } +3089 +3090 prev_packet = Some(packet); +3091 } 3092 -3093 prev_packet = Some(packet); -3094 } -3095 -3096 self.handle_lost_packets( -3097 pn_space, -3098 path_id, -3099 now, -3100 lost_packets, -3101 lost_mtu_probe, -3102 loss_delay, -3103 in_persistent_congestion, -3104 size_of_lost_packets, -3105 ); -3106 } -3107 -3108 /// Drops the path state, declaring any remaining in-flight packets as lost -3109 fn discard_path(&mut self, path_id: PathId, now: Instant) { -3110 trace!(%path_id, "dropping path state"); -3111 let path = self.path_data(path_id); -3112 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -3113 -3114 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; -3115 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] -3116 .for_path(path_id) -3117 .sent_packets -3118 .iter() -3119 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) -3120 .map(|(pn, info)| { -3121 size_of_lost_packets += info.size as u64; -3122 pn -3123 }) -3124 .collect(); -3125 -3126 if !lost_pns.is_empty() { -3127 trace!( -3128 %path_id, -3129 count = lost_pns.len(), -3130 lost_bytes = size_of_lost_packets, -3131 "packets lost on path abandon" -3132 ); -3133 self.handle_lost_packets( -3134 SpaceId::Data, -3135 path_id, -3136 now, -3137 lost_pns, -3138 in_flight_mtu_probe, -3139 Duration::ZERO, -3140 false, -3141 size_of_lost_packets, -3142 ); -3143 } -3144 // Before removing the path, we fetch the final path stats via `Self::path_stats`. -3145 // This updates some values for the last time. -3146 let path_stats = self.path_stats(path_id).unwrap_or_default(); -3147 self.path_stats.remove(&path_id); -3148 self.paths.remove(&path_id); -3149 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); -3150 -3151 self.events.push_back( -3152 PathEvent::Abandoned { -3153 id: path_id, -3154 path_stats, -3155 } -3156 .into(), -3157 ); -3158 } -3159 -3160 fn handle_lost_packets( -3161 &mut self, -3162 pn_space: SpaceId, -3163 path_id: PathId, -3164 now: Instant, -3165 lost_packets: Vec<u64>, -3166 lost_mtu_probe: Option<u64>, -3167 loss_delay: Duration, -3168 in_persistent_congestion: bool, -3169 size_of_lost_packets: u64, -3170 ) { -3171 debug_assert!( -3172 { -3173 let mut sorted = lost_packets.clone(); -3174 sorted.sort(); -3175 sorted == lost_packets -3176 }, -3177 "lost_packets must be sorted" -3178 ); -3179 -3180 self.drain_lost_packets(now, pn_space, path_id); -3181 -3182 // OnPacketsLost -3183 if let Some(largest_lost) = lost_packets.last().cloned() { -3184 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; -3185 let largest_lost_sent = self.spaces[pn_space] -3186 .for_path(path_id) -3187 .sent_packets -3188 .get(largest_lost) -3189 .unwrap() -3190 .time_sent; -3191 let path_stats = self.path_stats.entry(path_id).or_default(); -3192 path_stats.lost_packets += lost_packets.len() as u64; -3193 path_stats.lost_bytes += size_of_lost_packets; -3194 trace!( -3195 %path_id, -3196 count = lost_packets.len(), -3197 lost_bytes = size_of_lost_packets, -3198 "packets lost", -3199 ); -3200 -3201 for &packet in &lost_packets { -3202 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { -3203 continue; -3204 }; -3205 self.qlog -3206 .emit_packet_lost(packet, &info, loss_delay, pn_space.kind(), now); -3207 self.paths -3208 .get_mut(&path_id) -3209 .unwrap() -3210 .remove_in_flight(&info); -3211 -3212 for frame in info.stream_frames { -3213 self.streams.retransmit(frame); -3214 } -3215 self.spaces[pn_space].pending |= info.retransmits; -3216 self.path_data_mut(path_id) -3217 .mtud -3218 .on_non_probe_lost(packet, info.size); -3219 -3220 self.spaces[pn_space].for_path(path_id).lost_packets.insert( -3221 packet, -3222 LostPacket { -3223 time_sent: info.time_sent, -3224 }, -3225 ); -3226 } -3227 -3228 let path = self.path_data_mut(path_id); -3229 if path.mtud.black_hole_detected(now) { -3230 path.congestion.on_mtu_update(path.mtud.current_mtu()); -3231 if let Some(max_datagram_size) = self.datagrams().max_size() -3232 && self.datagrams.drop_oversized(max_datagram_size) -3233 && self.datagrams.send_blocked -3234 { -3235 self.datagrams.send_blocked = false; -3236 self.events.push_back(Event::DatagramsUnblocked); -3237 } -3238 self.path_stats -3239 .entry(path_id) -3240 .or_default() -3241 .black_holes_detected += 1; -3242 } -3243 -3244 // Don't apply congestion penalty for lost ack-only packets -3245 let lost_ack_eliciting = -3246 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; -3247 -3248 if lost_ack_eliciting { -3249 self.path_stats -3250 .entry(path_id) -3251 .or_default() -3252 .congestion_events += 1; -3253 self.path_data_mut(path_id).congestion.on_congestion_event( -3254 now, -3255 largest_lost_sent, -3256 in_persistent_congestion, -3257 false, -3258 size_of_lost_packets, -3259 ); -3260 } -3261 } -3262 -3263 // Handle a lost MTU probe -3264 if let Some(packet) = lost_mtu_probe { -3265 let info = self.spaces[SpaceId::Data] -3266 .for_path(path_id) -3267 .take(packet) -3268 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and -3269 // therefore must not have been removed yet -3270 self.paths -3271 .get_mut(&path_id) -3272 .unwrap() -3273 .remove_in_flight(&info); -3274 self.path_data_mut(path_id).mtud.on_probe_lost(); -3275 self.path_stats -3276 .entry(path_id) -3277 .or_default() -3278 .lost_plpmtud_probes += 1; -3279 } -3280 } -3281 -3282 /// Returns the earliest time packets should be declared lost for all spaces on a path. -3283 /// -3284 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the -3285 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. -3286 /// The time returned is when this packet should be declared lost. -3287 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { -3288 SpaceId::iter() -3289 .filter_map(|id| { -3290 self.spaces[id] -3291 .number_spaces -3292 .get(&path_id) -3293 .and_then(|pns| pns.loss_time) -3294 .map(|time| (time, id)) -3295 }) -3296 .min_by_key(|&(time, _)| time) -3297 } -3298 -3299 /// Returns the earliest next PTO should fire for all spaces on a path. -3300 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { -3301 let path = self.path(path_id)?; -3302 let pto_count = path.pto_count; -3303 let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT)); -3304 let mut duration = path.rtt.pto_base() * backoff; -3305 -3306 if path_id == PathId::ZERO -3307 && path.in_flight.ack_eliciting == 0 -3308 && !self.peer_completed_address_validation(PathId::ZERO) -3309 { -3310 // Address Validation during Connection Establishment: -3311 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a -3312 // deadlock if an Initial or Handshake packet from the server is lost and the -3313 // server can not send more due to its anti-amplification limit the client must -3314 // send another packet on PTO. -3315 let space = match self.highest_space { -3316 SpaceKind::Handshake => SpaceId::Handshake, -3317 _ => SpaceId::Initial, -3318 }; +3093 self.handle_lost_packets( +3094 pn_space, +3095 path_id, +3096 now, +3097 lost_packets, +3098 lost_mtu_probe, +3099 loss_delay, +3100 in_persistent_congestion, +3101 size_of_lost_packets, +3102 ); +3103 } +3104 +3105 /// Drops the path state, declaring any remaining in-flight packets as lost +3106 fn discard_path(&mut self, path_id: PathId, now: Instant) { +3107 trace!(%path_id, "dropping path state"); +3108 let path = self.path_data(path_id); +3109 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); +3110 +3111 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; +3112 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] +3113 .for_path(path_id) +3114 .sent_packets +3115 .iter() +3116 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) +3117 .map(|(pn, info)| { +3118 size_of_lost_packets += info.size as u64; +3119 pn +3120 }) +3121 .collect(); +3122 +3123 if !lost_pns.is_empty() { +3124 trace!( +3125 %path_id, +3126 count = lost_pns.len(), +3127 lost_bytes = size_of_lost_packets, +3128 "packets lost on path abandon" +3129 ); +3130 self.handle_lost_packets( +3131 SpaceId::Data, +3132 path_id, +3133 now, +3134 lost_pns, +3135 in_flight_mtu_probe, +3136 Duration::ZERO, +3137 false, +3138 size_of_lost_packets, +3139 ); +3140 } +3141 // Before removing the path, we fetch the final path stats via `Self::path_stats`. +3142 // This updates some values for the last time. +3143 let path_stats = self.path_stats(path_id).unwrap_or_default(); +3144 self.path_stats.remove(&path_id); +3145 self.paths.remove(&path_id); +3146 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); +3147 +3148 self.events.push_back( +3149 PathEvent::Abandoned { +3150 id: path_id, +3151 path_stats, +3152 } +3153 .into(), +3154 ); +3155 } +3156 +3157 fn handle_lost_packets( +3158 &mut self, +3159 pn_space: SpaceId, +3160 path_id: PathId, +3161 now: Instant, +3162 lost_packets: Vec<u64>, +3163 lost_mtu_probe: Option<u64>, +3164 loss_delay: Duration, +3165 in_persistent_congestion: bool, +3166 size_of_lost_packets: u64, +3167 ) { +3168 debug_assert!( +3169 { +3170 let mut sorted = lost_packets.clone(); +3171 sorted.sort(); +3172 sorted == lost_packets +3173 }, +3174 "lost_packets must be sorted" +3175 ); +3176 +3177 self.drain_lost_packets(now, pn_space, path_id); +3178 +3179 // OnPacketsLost +3180 if let Some(largest_lost) = lost_packets.last().cloned() { +3181 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; +3182 let largest_lost_sent = self.spaces[pn_space] +3183 .for_path(path_id) +3184 .sent_packets +3185 .get(largest_lost) +3186 .unwrap() +3187 .time_sent; +3188 let path_stats = self.path_stats.entry(path_id).or_default(); +3189 path_stats.lost_packets += lost_packets.len() as u64; +3190 path_stats.lost_bytes += size_of_lost_packets; +3191 trace!( +3192 %path_id, +3193 count = lost_packets.len(), +3194 lost_bytes = size_of_lost_packets, +3195 "packets lost", +3196 ); +3197 +3198 for &packet in &lost_packets { +3199 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { +3200 continue; +3201 }; +3202 self.qlog +3203 .emit_packet_lost(packet, &info, loss_delay, pn_space.kind(), now); +3204 self.paths +3205 .get_mut(&path_id) +3206 .unwrap() +3207 .remove_in_flight(&info); +3208 +3209 for frame in info.stream_frames { +3210 self.streams.retransmit(frame); +3211 } +3212 self.spaces[pn_space].pending |= info.retransmits; +3213 self.path_data_mut(path_id) +3214 .mtud +3215 .on_non_probe_lost(packet, info.size); +3216 +3217 self.spaces[pn_space].for_path(path_id).lost_packets.insert( +3218 packet, +3219 LostPacket { +3220 time_sent: info.time_sent, +3221 }, +3222 ); +3223 } +3224 +3225 let path = self.path_data_mut(path_id); +3226 if path.mtud.black_hole_detected(now) { +3227 path.congestion.on_mtu_update(path.mtud.current_mtu()); +3228 if let Some(max_datagram_size) = self.datagrams().max_size() +3229 && self.datagrams.drop_oversized(max_datagram_size) +3230 && self.datagrams.send_blocked +3231 { +3232 self.datagrams.send_blocked = false; +3233 self.events.push_back(Event::DatagramsUnblocked); +3234 } +3235 self.path_stats +3236 .entry(path_id) +3237 .or_default() +3238 .black_holes_detected += 1; +3239 } +3240 +3241 // Don't apply congestion penalty for lost ack-only packets +3242 let lost_ack_eliciting = +3243 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; +3244 +3245 if lost_ack_eliciting { +3246 self.path_stats +3247 .entry(path_id) +3248 .or_default() +3249 .congestion_events += 1; +3250 self.path_data_mut(path_id).congestion.on_congestion_event( +3251 now, +3252 largest_lost_sent, +3253 in_persistent_congestion, +3254 false, +3255 size_of_lost_packets, +3256 ); +3257 } +3258 } +3259 +3260 // Handle a lost MTU probe +3261 if let Some(packet) = lost_mtu_probe { +3262 let info = self.spaces[SpaceId::Data] +3263 .for_path(path_id) +3264 .take(packet) +3265 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and +3266 // therefore must not have been removed yet +3267 self.paths +3268 .get_mut(&path_id) +3269 .unwrap() +3270 .remove_in_flight(&info); +3271 self.path_data_mut(path_id).mtud.on_probe_lost(); +3272 self.path_stats +3273 .entry(path_id) +3274 .or_default() +3275 .lost_plpmtud_probes += 1; +3276 } +3277 } +3278 +3279 /// Returns the earliest time packets should be declared lost for all spaces on a path. +3280 /// +3281 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the +3282 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. +3283 /// The time returned is when this packet should be declared lost. +3284 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { +3285 SpaceId::iter() +3286 .filter_map(|id| { +3287 self.spaces[id] +3288 .number_spaces +3289 .get(&path_id) +3290 .and_then(|pns| pns.loss_time) +3291 .map(|time| (time, id)) +3292 }) +3293 .min_by_key(|&(time, _)| time) +3294 } +3295 +3296 /// Returns the earliest next PTO should fire for all spaces on a path. +3297 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { +3298 let path = self.path(path_id)?; +3299 let pto_count = path.pto_count; +3300 let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT)); +3301 let mut duration = path.rtt.pto_base() * backoff; +3302 +3303 if path_id == PathId::ZERO +3304 && path.in_flight.ack_eliciting == 0 +3305 && !self.peer_completed_address_validation(PathId::ZERO) +3306 { +3307 // Address Validation during Connection Establishment: +3308 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a +3309 // deadlock if an Initial or Handshake packet from the server is lost and the +3310 // server can not send more due to its anti-amplification limit the client must +3311 // send another packet on PTO. +3312 let space = match self.highest_space { +3313 SpaceKind::Handshake => SpaceId::Handshake, +3314 _ => SpaceId::Initial, +3315 }; +3316 +3317 return Some((now + duration, space)); +3318 } 3319 -3320 return Some((now + duration, space)); -3321 } -3322 -3323 let mut result = None; -3324 for space in SpaceId::iter() { -3325 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { -3326 continue; -3327 }; -3328 -3329 if !pns.has_in_flight() { -3330 continue; -3331 } -3332 if space == SpaceId::Data { -3333 // Skip ApplicationData until handshake completes. -3334 if self.is_handshaking() { -3335 return result; -3336 } -3337 // Include max_ack_delay and backoff for ApplicationData. -3338 duration += self.ack_frequency.max_ack_delay_for_pto() * backoff; -3339 } -3340 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { -3341 continue; -3342 }; -3343 let pto = last_ack_eliciting + duration; -3344 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { -3345 if path.anti_amplification_blocked(1) { -3346 // Nothing would be able to be sent. -3347 continue; -3348 } -3349 if path.in_flight.ack_eliciting == 0 { -3350 // Nothing ack-eliciting, no PTO to arm/fire. -3351 continue; -3352 } -3353 result = Some((pto, space)); -3354 } -3355 } -3356 result -3357 } -3358 -3359 fn peer_completed_address_validation(&self, path: PathId) -> bool { -3360 // TODO(flub): This logic needs updating for multipath -3361 if self.side.is_server() || self.state.is_closed() { -3362 return true; -3363 } -3364 // The server is guaranteed to have validated our address if any of our handshake or 1-RTT -3365 // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys. -3366 self.spaces[SpaceId::Handshake] -3367 .path_space(PathId::ZERO) -3368 .and_then(|pns| pns.largest_acked_packet) -3369 .is_some() -3370 || self.spaces[SpaceId::Data] -3371 .path_space(path) -3372 .and_then(|pns| pns.largest_acked_packet) -3373 .is_some() -3374 || (self.crypto_state.has_keys(EncryptionLevel::OneRtt) -3375 && !self.crypto_state.has_keys(EncryptionLevel::Handshake)) -3376 } -3377 -3378 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed -3379 /// -3380 /// The timer must fire if either: -3381 /// - An ack-eliciting packet we sent needs to be declared lost. -3382 /// - A tail-loss probe needs to be sent. -3383 /// -3384 /// See [`Connection::on_loss_detection_timeout`] for details. -3385 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { -3386 if self.state.is_closed() { -3387 // No loss detection takes place on closed connections, and `close_common` already -3388 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a -3389 // reordered packet being handled by state-insensitive code. -3390 return; -3391 } -3392 -3393 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { -3394 // Time threshold loss detection. -3395 self.timers.set( -3396 Timer::PerPath(path_id, PathTimer::LossDetection), -3397 loss_time, -3398 self.qlog.with_time(now), -3399 ); -3400 return; -3401 } -3402 -3403 // Determine which PN space to arm PTO for. -3404 // Calculate PTO duration -3405 if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) { -3406 self.timers.set( -3407 Timer::PerPath(path_id, PathTimer::LossDetection), -3408 timeout, -3409 self.qlog.with_time(now), -3410 ); -3411 } else { -3412 self.timers.stop( -3413 Timer::PerPath(path_id, PathTimer::LossDetection), -3414 self.qlog.with_time(now), -3415 ); -3416 } -3417 } -3418 -3419 /// The maximum probe timeout across all paths -3420 /// -3421 /// See [`Connection::pto`] -3422 fn max_pto_for_space(&self, space: SpaceKind) -> Duration { -3423 self.paths -3424 .keys() -3425 .map(|path_id| self.pto(space, *path_id)) -3426 .max() -3427 .expect("there should be at least one path") -3428 } -3429 -3430 /// Probe Timeout -3431 /// -3432 /// The PTO is logically the time in which you'd expect to receive an acknowledgement -3433 /// for a packet. So approximately RTT + max_ack_delay. -3434 fn pto(&self, space: SpaceKind, path_id: PathId) -> Duration { -3435 let max_ack_delay = match space { -3436 SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO, -3437 SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(), -3438 }; -3439 self.path_data(path_id).rtt.pto_base() + max_ack_delay -3440 } -3441 -3442 fn on_packet_authenticated( -3443 &mut self, -3444 now: Instant, -3445 space_id: SpaceKind, -3446 path_id: PathId, -3447 ecn: Option<EcnCodepoint>, -3448 packet: Option<u64>, -3449 spin: bool, -3450 is_1rtt: bool, -3451 ) { -3452 self.total_authed_packets += 1; -3453 self.reset_keep_alive(path_id, now); -3454 self.reset_idle_timeout(now, space_id, path_id); -3455 self.permit_idle_reset = true; -3456 self.receiving_ecn |= ecn.is_some(); -3457 if let Some(x) = ecn { -3458 let space = &mut self.spaces[space_id]; -3459 space.for_path(path_id).ecn_counters += x; -3460 -3461 if x.is_ce() { -3462 space -3463 .for_path(path_id) -3464 .pending_acks -3465 .set_immediate_ack_required(); -3466 } -3467 } -3468 -3469 let packet = match packet { -3470 Some(x) => x, -3471 None => return, -3472 }; -3473 match &self.side { -3474 ConnectionSide::Client { .. } => { -3475 // If we received a handshake packet that authenticated, then we're talking to -3476 // the real server. From now on we should no longer allow the server to migrate -3477 // its address. -3478 if space_id == SpaceKind::Handshake -3479 && let Some(hs) = self.state.as_handshake_mut() -3480 { -3481 hs.allow_server_migration = false; -3482 } -3483 } -3484 ConnectionSide::Server { .. } => { -3485 if self.crypto_state.has_keys(EncryptionLevel::Initial) -3486 && space_id == SpaceKind::Handshake -3487 { -3488 // A server stops sending and processing Initial packets when it receives its first Handshake packet. -3489 self.discard_space(now, SpaceKind::Initial); -3490 } -3491 if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) && is_1rtt { -3492 // Discard 0-RTT keys soon after receiving a 1-RTT packet -3493 self.set_key_discard_timer(now, space_id) -3494 } -3495 } -3496 } -3497 let space = self.spaces[space_id].for_path(path_id); -3498 space.pending_acks.insert_one(packet, now); -3499 if packet >= space.rx_packet.unwrap_or_default() { -3500 space.rx_packet = Some(packet); -3501 // Update outgoing spin bit, inverting iff we're the client -3502 self.spin = self.side.is_client() ^ spin; -3503 } -3504 } -3505 -3506 /// Resets the idle timeout timers -3507 /// -3508 /// Without multipath there is only the connection-wide idle timeout. When multipath is -3509 /// enabled there is an additional per-path idle timeout. -3510 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceKind, path_id: PathId) { -3511 // First reset the global idle timeout. -3512 if let Some(timeout) = self.idle_timeout { -3513 if self.state.is_closed() { -3514 self.timers -3515 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); -3516 } else { -3517 let dt = cmp::max(timeout, 3 * self.max_pto_for_space(space)); -3518 self.timers.set( -3519 Timer::Conn(ConnTimer::Idle), -3520 now + dt, -3521 self.qlog.with_time(now), -3522 ); -3523 } -3524 } -3525 -3526 // Now handle the per-path state -3527 if let Some(timeout) = self.path_data(path_id).idle_timeout { -3528 if self.state.is_closed() { -3529 self.timers.stop( -3530 Timer::PerPath(path_id, PathTimer::PathIdle), -3531 self.qlog.with_time(now), -3532 ); -3533 } else { -3534 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); -3535 self.timers.set( -3536 Timer::PerPath(path_id, PathTimer::PathIdle), -3537 now + dt, -3538 self.qlog.with_time(now), -3539 ); -3540 } -3541 } -3542 } -3543 -3544 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers -3545 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { -3546 if !self.state.is_established() { -3547 return; -3548 } -3549 -3550 if let Some(interval) = self.config.keep_alive_interval { -3551 self.timers.set( -3552 Timer::Conn(ConnTimer::KeepAlive), -3553 now + interval, -3554 self.qlog.with_time(now), -3555 ); -3556 } -3557 -3558 if let Some(interval) = self.path_data(path_id).keep_alive { -3559 self.timers.set( -3560 Timer::PerPath(path_id, PathTimer::PathKeepAlive), -3561 now + interval, -3562 self.qlog.with_time(now), -3563 ); -3564 } -3565 } -3566 -3567 /// Sets the timer for when a previously issued CID should be retired next -3568 fn reset_cid_retirement(&mut self, now: Instant) { -3569 if let Some((_path, t)) = self.next_cid_retirement() { -3570 self.timers.set( -3571 Timer::Conn(ConnTimer::PushNewCid), -3572 t, -3573 self.qlog.with_time(now), -3574 ); -3575 } -3576 } -3577 -3578 /// The next time when a previously issued CID should be retired -3579 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { -3580 self.local_cid_state -3581 .iter() -3582 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) -3583 .min_by_key(|(_path_id, timeout)| *timeout) -3584 } -3585 -3586 /// Handle the already-decrypted first packet from the client -3587 /// -3588 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more -3589 /// efficient. -3590 pub(crate) fn handle_first_packet( -3591 &mut self, -3592 now: Instant, -3593 network_path: FourTuple, -3594 ecn: Option<EcnCodepoint>, -3595 packet_number: u64, -3596 packet: InitialPacket, -3597 remaining: Option<BytesMut>, -3598 ) -> Result<(), ConnectionError> { -3599 let span = trace_span!("first recv"); -3600 let _guard = span.enter(); -3601 debug_assert!(self.side.is_server()); -3602 let len = packet.header_data.len() + packet.payload.len(); -3603 let path_id = PathId::ZERO; -3604 self.path_data_mut(path_id).total_recvd = len as u64; -3605 -3606 if let Some(hs) = self.state.as_handshake_mut() { -3607 hs.expected_token = packet.header.token.clone(); -3608 } else { -3609 unreachable!("first packet must be delivered in Handshake state"); -3610 } -3611 -3612 // The first packet is always on PathId::ZERO -3613 self.on_packet_authenticated( -3614 now, -3615 SpaceKind::Initial, -3616 path_id, -3617 ecn, -3618 Some(packet_number), -3619 false, -3620 false, -3621 ); -3622 -3623 let packet: Packet = packet.into(); +3320 let mut result = None; +3321 for space in SpaceId::iter() { +3322 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { +3323 continue; +3324 }; +3325 +3326 if !pns.has_in_flight() { +3327 continue; +3328 } +3329 if space == SpaceId::Data { +3330 // Skip ApplicationData until handshake completes. +3331 if self.is_handshaking() { +3332 return result; +3333 } +3334 // Include max_ack_delay and backoff for ApplicationData. +3335 duration += self.ack_frequency.max_ack_delay_for_pto() * backoff; +3336 } +3337 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { +3338 continue; +3339 }; +3340 let pto = last_ack_eliciting + duration; +3341 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { +3342 if path.anti_amplification_blocked(1) { +3343 // Nothing would be able to be sent. +3344 continue; +3345 } +3346 if path.in_flight.ack_eliciting == 0 { +3347 // Nothing ack-eliciting, no PTO to arm/fire. +3348 continue; +3349 } +3350 result = Some((pto, space)); +3351 } +3352 } +3353 result +3354 } +3355 +3356 fn peer_completed_address_validation(&self, path: PathId) -> bool { +3357 // TODO(flub): This logic needs updating for multipath +3358 if self.side.is_server() || self.state.is_closed() { +3359 return true; +3360 } +3361 // The server is guaranteed to have validated our address if any of our handshake or 1-RTT +3362 // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys. +3363 self.spaces[SpaceId::Handshake] +3364 .path_space(PathId::ZERO) +3365 .and_then(|pns| pns.largest_acked_packet) +3366 .is_some() +3367 || self.spaces[SpaceId::Data] +3368 .path_space(path) +3369 .and_then(|pns| pns.largest_acked_packet) +3370 .is_some() +3371 || (self.crypto_state.has_keys(EncryptionLevel::OneRtt) +3372 && !self.crypto_state.has_keys(EncryptionLevel::Handshake)) +3373 } +3374 +3375 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed +3376 /// +3377 /// The timer must fire if either: +3378 /// - An ack-eliciting packet we sent needs to be declared lost. +3379 /// - A tail-loss probe needs to be sent. +3380 /// +3381 /// See [`Connection::on_loss_detection_timeout`] for details. +3382 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { +3383 if self.state.is_closed() { +3384 // No loss detection takes place on closed connections, and `close_common` already +3385 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a +3386 // reordered packet being handled by state-insensitive code. +3387 return; +3388 } +3389 +3390 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { +3391 // Time threshold loss detection. +3392 self.timers.set( +3393 Timer::PerPath(path_id, PathTimer::LossDetection), +3394 loss_time, +3395 self.qlog.with_time(now), +3396 ); +3397 return; +3398 } +3399 +3400 // Determine which PN space to arm PTO for. +3401 // Calculate PTO duration +3402 if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) { +3403 self.timers.set( +3404 Timer::PerPath(path_id, PathTimer::LossDetection), +3405 timeout, +3406 self.qlog.with_time(now), +3407 ); +3408 } else { +3409 self.timers.stop( +3410 Timer::PerPath(path_id, PathTimer::LossDetection), +3411 self.qlog.with_time(now), +3412 ); +3413 } +3414 } +3415 +3416 /// The maximum probe timeout across all paths +3417 /// +3418 /// See [`Connection::pto`] +3419 fn max_pto_for_space(&self, space: SpaceKind) -> Duration { +3420 self.paths +3421 .keys() +3422 .map(|path_id| self.pto(space, *path_id)) +3423 .max() +3424 .expect("there should be at least one path") +3425 } +3426 +3427 /// Probe Timeout +3428 /// +3429 /// The PTO is logically the time in which you'd expect to receive an acknowledgement +3430 /// for a packet. So approximately RTT + max_ack_delay. +3431 fn pto(&self, space: SpaceKind, path_id: PathId) -> Duration { +3432 let max_ack_delay = match space { +3433 SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO, +3434 SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(), +3435 }; +3436 self.path_data(path_id).rtt.pto_base() + max_ack_delay +3437 } +3438 +3439 fn on_packet_authenticated( +3440 &mut self, +3441 now: Instant, +3442 space_id: SpaceKind, +3443 path_id: PathId, +3444 ecn: Option<EcnCodepoint>, +3445 packet: Option<u64>, +3446 spin: bool, +3447 is_1rtt: bool, +3448 ) { +3449 self.total_authed_packets += 1; +3450 self.reset_keep_alive(path_id, now); +3451 self.reset_idle_timeout(now, space_id, path_id); +3452 self.permit_idle_reset = true; +3453 self.receiving_ecn |= ecn.is_some(); +3454 if let Some(x) = ecn { +3455 let space = &mut self.spaces[space_id]; +3456 space.for_path(path_id).ecn_counters += x; +3457 +3458 if x.is_ce() { +3459 space +3460 .for_path(path_id) +3461 .pending_acks +3462 .set_immediate_ack_required(); +3463 } +3464 } +3465 +3466 let packet = match packet { +3467 Some(x) => x, +3468 None => return, +3469 }; +3470 match &self.side { +3471 ConnectionSide::Client { .. } => { +3472 // If we received a handshake packet that authenticated, then we're talking to +3473 // the real server. From now on we should no longer allow the server to migrate +3474 // its address. +3475 if space_id == SpaceKind::Handshake +3476 && let Some(hs) = self.state.as_handshake_mut() +3477 { +3478 hs.allow_server_migration = false; +3479 } +3480 } +3481 ConnectionSide::Server { .. } => { +3482 if self.crypto_state.has_keys(EncryptionLevel::Initial) +3483 && space_id == SpaceKind::Handshake +3484 { +3485 // A server stops sending and processing Initial packets when it receives its first Handshake packet. +3486 self.discard_space(now, SpaceKind::Initial); +3487 } +3488 if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) && is_1rtt { +3489 // Discard 0-RTT keys soon after receiving a 1-RTT packet +3490 self.set_key_discard_timer(now, space_id) +3491 } +3492 } +3493 } +3494 let space = self.spaces[space_id].for_path(path_id); +3495 space.pending_acks.insert_one(packet, now); +3496 if packet >= space.rx_packet.unwrap_or_default() { +3497 space.rx_packet = Some(packet); +3498 // Update outgoing spin bit, inverting iff we're the client +3499 self.spin = self.side.is_client() ^ spin; +3500 } +3501 } +3502 +3503 /// Resets the idle timeout timers +3504 /// +3505 /// Without multipath there is only the connection-wide idle timeout. When multipath is +3506 /// enabled there is an additional per-path idle timeout. +3507 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceKind, path_id: PathId) { +3508 // First reset the global idle timeout. +3509 if let Some(timeout) = self.idle_timeout { +3510 if self.state.is_closed() { +3511 self.timers +3512 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); +3513 } else { +3514 let dt = cmp::max(timeout, 3 * self.max_pto_for_space(space)); +3515 self.timers.set( +3516 Timer::Conn(ConnTimer::Idle), +3517 now + dt, +3518 self.qlog.with_time(now), +3519 ); +3520 } +3521 } +3522 +3523 // Now handle the per-path state +3524 if let Some(timeout) = self.path_data(path_id).idle_timeout { +3525 if self.state.is_closed() { +3526 self.timers.stop( +3527 Timer::PerPath(path_id, PathTimer::PathIdle), +3528 self.qlog.with_time(now), +3529 ); +3530 } else { +3531 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); +3532 self.timers.set( +3533 Timer::PerPath(path_id, PathTimer::PathIdle), +3534 now + dt, +3535 self.qlog.with_time(now), +3536 ); +3537 } +3538 } +3539 } +3540 +3541 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers +3542 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { +3543 if !self.state.is_established() { +3544 return; +3545 } +3546 +3547 if let Some(interval) = self.config.keep_alive_interval { +3548 self.timers.set( +3549 Timer::Conn(ConnTimer::KeepAlive), +3550 now + interval, +3551 self.qlog.with_time(now), +3552 ); +3553 } +3554 +3555 if let Some(interval) = self.path_data(path_id).keep_alive { +3556 self.timers.set( +3557 Timer::PerPath(path_id, PathTimer::PathKeepAlive), +3558 now + interval, +3559 self.qlog.with_time(now), +3560 ); +3561 } +3562 } +3563 +3564 /// Sets the timer for when a previously issued CID should be retired next +3565 fn reset_cid_retirement(&mut self, now: Instant) { +3566 if let Some((_path, t)) = self.next_cid_retirement() { +3567 self.timers.set( +3568 Timer::Conn(ConnTimer::PushNewCid), +3569 t, +3570 self.qlog.with_time(now), +3571 ); +3572 } +3573 } +3574 +3575 /// The next time when a previously issued CID should be retired +3576 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { +3577 self.local_cid_state +3578 .iter() +3579 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) +3580 .min_by_key(|(_path_id, timeout)| *timeout) +3581 } +3582 +3583 /// Handle the already-decrypted first packet from the client +3584 /// +3585 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more +3586 /// efficient. +3587 pub(crate) fn handle_first_packet( +3588 &mut self, +3589 now: Instant, +3590 network_path: FourTuple, +3591 ecn: Option<EcnCodepoint>, +3592 packet_number: u64, +3593 packet: InitialPacket, +3594 remaining: Option<BytesMut>, +3595 ) -> Result<(), ConnectionError> { +3596 let span = trace_span!("first recv"); +3597 let _guard = span.enter(); +3598 debug_assert!(self.side.is_server()); +3599 let len = packet.header_data.len() + packet.payload.len(); +3600 let path_id = PathId::ZERO; +3601 self.path_data_mut(path_id).total_recvd = len as u64; +3602 +3603 if let Some(hs) = self.state.as_handshake_mut() { +3604 hs.expected_token = packet.header.token.clone(); +3605 } else { +3606 unreachable!("first packet must be delivered in Handshake state"); +3607 } +3608 +3609 // The first packet is always on PathId::ZERO +3610 self.on_packet_authenticated( +3611 now, +3612 SpaceKind::Initial, +3613 path_id, +3614 ecn, +3615 Some(packet_number), +3616 false, +3617 false, +3618 ); +3619 +3620 let packet: Packet = packet.into(); +3621 +3622 let mut qlog = QlogRecvPacket::new(len); +3623 qlog.header(&packet.header, Some(packet_number), path_id); 3624 -3625 let mut qlog = QlogRecvPacket::new(len); -3626 qlog.header(&packet.header, Some(packet_number), path_id); -3627 -3628 self.process_decrypted_packet( -3629 now, -3630 network_path, -3631 path_id, -3632 Some(packet_number), -3633 packet, -3634 &mut qlog, -3635 )?; -3636 self.qlog.emit_packet_received(qlog, now); -3637 if let Some(data) = remaining { -3638 self.handle_coalesced(now, network_path, path_id, ecn, data); -3639 } -3640 -3641 self.qlog.emit_recovery_metrics( -3642 path_id, -3643 &mut self.paths.get_mut(&path_id).unwrap().data, -3644 now, -3645 ); +3625 self.process_decrypted_packet( +3626 now, +3627 network_path, +3628 path_id, +3629 Some(packet_number), +3630 packet, +3631 &mut qlog, +3632 )?; +3633 self.qlog.emit_packet_received(qlog, now); +3634 if let Some(data) = remaining { +3635 self.handle_coalesced(now, network_path, path_id, ecn, data); +3636 } +3637 +3638 self.qlog.emit_recovery_metrics( +3639 path_id, +3640 &mut self.paths.get_mut(&path_id).unwrap().data, +3641 now, +3642 ); +3643 +3644 Ok(()) +3645 } 3646 -3647 Ok(()) -3648 } -3649 -3650 fn init_0rtt(&mut self, now: Instant) { -3651 let (header, packet) = match self.crypto_state.session.early_crypto() { -3652 Some(x) => x, -3653 None => return, -3654 }; -3655 if self.side.is_client() { -3656 match self.crypto_state.session.transport_parameters() { -3657 Ok(params) => { -3658 let params = params -3659 .expect("crypto layer didn't supply transport parameters with ticket"); -3660 // Certain values must not be cached -3661 let params = TransportParameters { -3662 initial_src_cid: None, -3663 original_dst_cid: None, -3664 preferred_address: None, -3665 retry_src_cid: None, -3666 stateless_reset_token: None, -3667 min_ack_delay: None, -3668 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, -3669 max_ack_delay: TransportParameters::default().max_ack_delay, -3670 initial_max_path_id: None, -3671 ..params -3672 }; -3673 self.set_peer_params(params); -3674 self.qlog.emit_peer_transport_params_restored(self, now); -3675 } -3676 Err(e) => { -3677 error!("session ticket has malformed transport parameters: {}", e); -3678 return; -3679 } -3680 } -3681 } -3682 trace!("0-RTT enabled"); -3683 self.crypto_state.enable_zero_rtt(header, packet); -3684 } -3685 -3686 fn read_crypto( -3687 &mut self, -3688 space: SpaceId, -3689 crypto: &frame::Crypto, -3690 payload_len: usize, -3691 ) -> Result<(), TransportError> { -3692 let expected = if !self.state.is_handshake() { -3693 SpaceId::Data -3694 } else if self.highest_space == SpaceKind::Initial { -3695 SpaceId::Initial -3696 } else { -3697 // On the server, self.highest_space can be Data after receiving the client's first -3698 // flight, but we expect Handshake CRYPTO until the handshake is complete. -3699 SpaceId::Handshake -3700 }; -3701 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT -3702 // packets are illegal, and we don't process 1-RTT packets until the handshake is -3703 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. -3704 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); -3705 -3706 let end = crypto.offset + crypto.data.len() as u64; -3707 if space < expected -3708 && end -3709 > self.crypto_state.spaces[space.kind()] -3710 .crypto_stream -3711 .bytes_read() -3712 { -3713 warn!( -3714 "received new {:?} CRYPTO data when expecting {:?}", -3715 space, expected -3716 ); -3717 return Err(TransportError::PROTOCOL_VIOLATION( -3718 "new data at unexpected encryption level", -3719 )); -3720 } -3721 -3722 let crypto_space = &mut self.crypto_state.spaces[space.kind()]; -3723 let max = end.saturating_sub(crypto_space.crypto_stream.bytes_read()); -3724 if max > self.config.crypto_buffer_size as u64 { -3725 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); -3726 } -3727 -3728 crypto_space -3729 .crypto_stream -3730 .insert(crypto.offset, crypto.data.clone(), payload_len); -3731 while let Some(chunk) = crypto_space.crypto_stream.read(usize::MAX, true) { -3732 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); -3733 if self.crypto_state.session.read_handshake(&chunk.bytes)? { -3734 self.events.push_back(Event::HandshakeDataReady); -3735 } -3736 } +3647 fn init_0rtt(&mut self, now: Instant) { +3648 let (header, packet) = match self.crypto_state.session.early_crypto() { +3649 Some(x) => x, +3650 None => return, +3651 }; +3652 if self.side.is_client() { +3653 match self.crypto_state.session.transport_parameters() { +3654 Ok(params) => { +3655 let params = params +3656 .expect("crypto layer didn't supply transport parameters with ticket"); +3657 // Certain values must not be cached +3658 let params = TransportParameters { +3659 initial_src_cid: None, +3660 original_dst_cid: None, +3661 preferred_address: None, +3662 retry_src_cid: None, +3663 stateless_reset_token: None, +3664 min_ack_delay: None, +3665 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, +3666 max_ack_delay: TransportParameters::default().max_ack_delay, +3667 initial_max_path_id: None, +3668 ..params +3669 }; +3670 self.set_peer_params(params); +3671 self.qlog.emit_peer_transport_params_restored(self, now); +3672 } +3673 Err(e) => { +3674 error!("session ticket has malformed transport parameters: {}", e); +3675 return; +3676 } +3677 } +3678 } +3679 trace!("0-RTT enabled"); +3680 self.crypto_state.enable_zero_rtt(header, packet); +3681 } +3682 +3683 fn read_crypto( +3684 &mut self, +3685 space: SpaceId, +3686 crypto: &frame::Crypto, +3687 payload_len: usize, +3688 ) -> Result<(), TransportError> { +3689 let expected = if !self.state.is_handshake() { +3690 SpaceId::Data +3691 } else if self.highest_space == SpaceKind::Initial { +3692 SpaceId::Initial +3693 } else { +3694 // On the server, self.highest_space can be Data after receiving the client's first +3695 // flight, but we expect Handshake CRYPTO until the handshake is complete. +3696 SpaceId::Handshake +3697 }; +3698 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT +3699 // packets are illegal, and we don't process 1-RTT packets until the handshake is +3700 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. +3701 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); +3702 +3703 let end = crypto.offset + crypto.data.len() as u64; +3704 if space < expected +3705 && end +3706 > self.crypto_state.spaces[space.kind()] +3707 .crypto_stream +3708 .bytes_read() +3709 { +3710 warn!( +3711 "received new {:?} CRYPTO data when expecting {:?}", +3712 space, expected +3713 ); +3714 return Err(TransportError::PROTOCOL_VIOLATION( +3715 "new data at unexpected encryption level", +3716 )); +3717 } +3718 +3719 let crypto_space = &mut self.crypto_state.spaces[space.kind()]; +3720 let max = end.saturating_sub(crypto_space.crypto_stream.bytes_read()); +3721 if max > self.config.crypto_buffer_size as u64 { +3722 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); +3723 } +3724 +3725 crypto_space +3726 .crypto_stream +3727 .insert(crypto.offset, crypto.data.clone(), payload_len); +3728 while let Some(chunk) = crypto_space.crypto_stream.read(usize::MAX, true) { +3729 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); +3730 if self.crypto_state.session.read_handshake(&chunk.bytes)? { +3731 self.events.push_back(Event::HandshakeDataReady); +3732 } +3733 } +3734 +3735 Ok(()) +3736 } 3737 -3738 Ok(()) -3739 } -3740 -3741 fn write_crypto(&mut self) { -3742 loop { -3743 let space = self.highest_space; -3744 let mut outgoing = Vec::new(); -3745 if let Some(crypto) = self.crypto_state.session.write_handshake(&mut outgoing) { -3746 match space { -3747 SpaceKind::Initial => { -3748 self.upgrade_crypto(SpaceKind::Handshake, crypto); +3738 fn write_crypto(&mut self) { +3739 loop { +3740 let space = self.highest_space; +3741 let mut outgoing = Vec::new(); +3742 if let Some(crypto) = self.crypto_state.session.write_handshake(&mut outgoing) { +3743 match space { +3744 SpaceKind::Initial => { +3745 self.upgrade_crypto(SpaceKind::Handshake, crypto); +3746 } +3747 SpaceKind::Handshake => { +3748 self.upgrade_crypto(SpaceKind::Data, crypto); 3749 } -3750 SpaceKind::Handshake => { -3751 self.upgrade_crypto(SpaceKind::Data, crypto); -3752 } -3753 SpaceKind::Data => unreachable!("got updated secrets during 1-RTT"), -3754 } -3755 } -3756 if outgoing.is_empty() { -3757 if space == self.highest_space { -3758 break; -3759 } else { -3760 // Keys updated, check for more data to send -3761 continue; -3762 } -3763 } -3764 let offset = self.crypto_state.spaces[space].crypto_offset; -3765 let outgoing = Bytes::from(outgoing); -3766 if let Some(hs) = self.state.as_handshake_mut() -3767 && space == SpaceKind::Initial -3768 && offset == 0 -3769 && self.side.is_client() -3770 { -3771 hs.client_hello = Some(outgoing.clone()); -3772 } -3773 self.crypto_state.spaces[space].crypto_offset += outgoing.len() as u64; -3774 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); -3775 self.spaces[space].pending.crypto.push_back(frame::Crypto { -3776 offset, -3777 data: outgoing, -3778 }); -3779 } -3780 } -3781 -3782 /// Switch to stronger cryptography during handshake -3783 fn upgrade_crypto(&mut self, space: SpaceKind, crypto: Keys) { -3784 debug_assert!( -3785 !self.crypto_state.has_keys(space.encryption_level()), -3786 "already reached packet space {space:?}" -3787 ); -3788 trace!("{:?} keys ready", space); -3789 if space == SpaceKind::Data { -3790 // Precompute the first key update -3791 self.crypto_state.next_crypto = Some( -3792 self.crypto_state -3793 .session -3794 .next_1rtt_keys() -3795 .expect("handshake should be complete"), -3796 ); -3797 } -3798 -3799 self.crypto_state.spaces[space].keys = Some(crypto); -3800 debug_assert!(space > self.highest_space); -3801 self.highest_space = space; -3802 if space == SpaceKind::Data && self.side.is_client() { -3803 // Discard 0-RTT keys because 1-RTT keys are available. -3804 self.crypto_state.discard_zero_rtt(); -3805 } -3806 } -3807 -3808 fn discard_space(&mut self, now: Instant, space: SpaceKind) { -3809 debug_assert!(space != SpaceKind::Data); -3810 trace!("discarding {:?} keys", space); -3811 if space == SpaceKind::Initial { -3812 // No longer needed -3813 if let ConnectionSide::Client { token, .. } = &mut self.side { -3814 *token = Bytes::new(); -3815 } -3816 } -3817 self.crypto_state.spaces[space].keys = None; -3818 let space = &mut self.spaces[space]; -3819 let pns = space.for_path(PathId::ZERO); -3820 pns.time_of_last_ack_eliciting_packet = None; -3821 pns.loss_time = None; -3822 pns.loss_probes = 0; -3823 let sent_packets = mem::take(&mut pns.sent_packets); -3824 let path = self.paths.get_mut(&PathId::ZERO).unwrap(); -3825 for (_, packet) in sent_packets.into_iter() { -3826 path.data.remove_in_flight(&packet); -3827 } +3750 SpaceKind::Data => unreachable!("got updated secrets during 1-RTT"), +3751 } +3752 } +3753 if outgoing.is_empty() { +3754 if space == self.highest_space { +3755 break; +3756 } else { +3757 // Keys updated, check for more data to send +3758 continue; +3759 } +3760 } +3761 let offset = self.crypto_state.spaces[space].crypto_offset; +3762 let outgoing = Bytes::from(outgoing); +3763 if let Some(hs) = self.state.as_handshake_mut() +3764 && space == SpaceKind::Initial +3765 && offset == 0 +3766 && self.side.is_client() +3767 { +3768 hs.client_hello = Some(outgoing.clone()); +3769 } +3770 self.crypto_state.spaces[space].crypto_offset += outgoing.len() as u64; +3771 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); +3772 self.spaces[space].pending.crypto.push_back(frame::Crypto { +3773 offset, +3774 data: outgoing, +3775 }); +3776 } +3777 } +3778 +3779 /// Switch to stronger cryptography during handshake +3780 fn upgrade_crypto(&mut self, space: SpaceKind, crypto: Keys) { +3781 debug_assert!( +3782 !self.crypto_state.has_keys(space.encryption_level()), +3783 "already reached packet space {space:?}" +3784 ); +3785 trace!("{:?} keys ready", space); +3786 if space == SpaceKind::Data { +3787 // Precompute the first key update +3788 self.crypto_state.next_crypto = Some( +3789 self.crypto_state +3790 .session +3791 .next_1rtt_keys() +3792 .expect("handshake should be complete"), +3793 ); +3794 } +3795 +3796 self.crypto_state.spaces[space].keys = Some(crypto); +3797 debug_assert!(space > self.highest_space); +3798 self.highest_space = space; +3799 if space == SpaceKind::Data && self.side.is_client() { +3800 // Discard 0-RTT keys because 1-RTT keys are available. +3801 self.crypto_state.discard_zero_rtt(); +3802 } +3803 } +3804 +3805 fn discard_space(&mut self, now: Instant, space: SpaceKind) { +3806 debug_assert!(space != SpaceKind::Data); +3807 trace!("discarding {:?} keys", space); +3808 if space == SpaceKind::Initial { +3809 // No longer needed +3810 if let ConnectionSide::Client { token, .. } = &mut self.side { +3811 *token = Bytes::new(); +3812 } +3813 } +3814 self.crypto_state.spaces[space].keys = None; +3815 let space = &mut self.spaces[space]; +3816 let pns = space.for_path(PathId::ZERO); +3817 pns.time_of_last_ack_eliciting_packet = None; +3818 pns.loss_time = None; +3819 pns.loss_probes = 0; +3820 let sent_packets = mem::take(&mut pns.sent_packets); +3821 let path = self.paths.get_mut(&PathId::ZERO).unwrap(); +3822 for (_, packet) in sent_packets.into_iter() { +3823 path.data.remove_in_flight(&packet); +3824 } +3825 +3826 self.set_loss_detection_timer(now, PathId::ZERO) +3827 } 3828 -3829 self.set_loss_detection_timer(now, PathId::ZERO) -3830 } -3831 -3832 fn handle_coalesced( -3833 &mut self, -3834 now: Instant, -3835 network_path: FourTuple, -3836 path_id: PathId, -3837 ecn: Option<EcnCodepoint>, -3838 data: BytesMut, -3839 ) { -3840 self.path_data_mut(path_id) -3841 .inc_total_recvd(data.len() as u64); -3842 let mut remaining = Some(data); -3843 let cid_len = self -3844 .local_cid_state -3845 .values() -3846 .map(|cid_state| cid_state.cid_len()) -3847 .next() -3848 .expect("one cid_state must exist"); -3849 while let Some(data) = remaining { -3850 match PartialDecode::new( -3851 data, -3852 &FixedLengthConnectionIdParser::new(cid_len), -3853 &[self.version], -3854 self.endpoint_config.grease_quic_bit, -3855 ) { -3856 Ok((partial_decode, rest)) => { -3857 remaining = rest; -3858 self.handle_decode(now, network_path, path_id, ecn, partial_decode); -3859 } -3860 Err(e) => { -3861 trace!("malformed header: {}", e); -3862 return; -3863 } -3864 } -3865 } -3866 } -3867 -3868 fn handle_decode( -3869 &mut self, -3870 now: Instant, -3871 network_path: FourTuple, -3872 path_id: PathId, -3873 ecn: Option<EcnCodepoint>, -3874 partial_decode: PartialDecode, -3875 ) { -3876 let qlog = QlogRecvPacket::new(partial_decode.len()); -3877 if let Some(decoded) = self -3878 .crypto_state -3879 .unprotect_header(partial_decode, self.peer_params.stateless_reset_token) -3880 { -3881 self.handle_packet( -3882 now, -3883 network_path, -3884 path_id, -3885 ecn, -3886 decoded.packet, -3887 decoded.stateless_reset, -3888 qlog, -3889 ); -3890 } -3891 } -3892 -3893 fn handle_packet( -3894 &mut self, -3895 now: Instant, -3896 network_path: FourTuple, -3897 path_id: PathId, -3898 ecn: Option<EcnCodepoint>, -3899 packet: Option<Packet>, -3900 stateless_reset: bool, -3901 mut qlog: QlogRecvPacket, -3902 ) { -3903 self.stats.udp_rx.ios += 1; -3904 self.path_stats.entry(path_id).or_default().udp_rx.ios += 1; -3905 -3906 if let Some(ref packet) = packet { -3907 trace!( -3908 "got {:?} packet ({} bytes) from {} using id {}", -3909 packet.header.space(), -3910 packet.payload.len() + packet.header_data.len(), -3911 network_path, -3912 packet.header.dst_cid(), -3913 ); -3914 } -3915 -3916 if self.is_handshaking() { -3917 if path_id != PathId::ZERO { -3918 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); -3919 return; -3920 } -3921 if network_path != self.path_data_mut(path_id).network_path { -3922 if let Some(hs) = self.state.as_handshake() { -3923 if hs.allow_server_migration { -3924 trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote"); -3925 self.path_data_mut(path_id).network_path = network_path; -3926 self.qlog.emit_tuple_assigned(path_id, network_path, now); -3927 } else { -3928 debug!("discarding packet with unexpected remote during handshake"); -3929 return; -3930 } -3931 } else { -3932 debug!("discarding packet with unexpected remote during handshake"); -3933 return; -3934 } -3935 } -3936 } +3829 fn handle_coalesced( +3830 &mut self, +3831 now: Instant, +3832 network_path: FourTuple, +3833 path_id: PathId, +3834 ecn: Option<EcnCodepoint>, +3835 data: BytesMut, +3836 ) { +3837 self.path_data_mut(path_id) +3838 .inc_total_recvd(data.len() as u64); +3839 let mut remaining = Some(data); +3840 let cid_len = self +3841 .local_cid_state +3842 .values() +3843 .map(|cid_state| cid_state.cid_len()) +3844 .next() +3845 .expect("one cid_state must exist"); +3846 while let Some(data) = remaining { +3847 match PartialDecode::new( +3848 data, +3849 &FixedLengthConnectionIdParser::new(cid_len), +3850 &[self.version], +3851 self.endpoint_config.grease_quic_bit, +3852 ) { +3853 Ok((partial_decode, rest)) => { +3854 remaining = rest; +3855 self.handle_decode(now, network_path, path_id, ecn, partial_decode); +3856 } +3857 Err(e) => { +3858 trace!("malformed header: {}", e); +3859 return; +3860 } +3861 } +3862 } +3863 } +3864 +3865 fn handle_decode( +3866 &mut self, +3867 now: Instant, +3868 network_path: FourTuple, +3869 path_id: PathId, +3870 ecn: Option<EcnCodepoint>, +3871 partial_decode: PartialDecode, +3872 ) { +3873 let qlog = QlogRecvPacket::new(partial_decode.len()); +3874 if let Some(decoded) = self +3875 .crypto_state +3876 .unprotect_header(partial_decode, self.peer_params.stateless_reset_token) +3877 { +3878 self.handle_packet( +3879 now, +3880 network_path, +3881 path_id, +3882 ecn, +3883 decoded.packet, +3884 decoded.stateless_reset, +3885 qlog, +3886 ); +3887 } +3888 } +3889 +3890 fn handle_packet( +3891 &mut self, +3892 now: Instant, +3893 network_path: FourTuple, +3894 path_id: PathId, +3895 ecn: Option<EcnCodepoint>, +3896 packet: Option<Packet>, +3897 stateless_reset: bool, +3898 mut qlog: QlogRecvPacket, +3899 ) { +3900 self.stats.udp_rx.ios += 1; +3901 self.path_stats.entry(path_id).or_default().udp_rx.ios += 1; +3902 +3903 if let Some(ref packet) = packet { +3904 trace!( +3905 "got {:?} packet ({} bytes) from {} using id {}", +3906 packet.header.space(), +3907 packet.payload.len() + packet.header_data.len(), +3908 network_path, +3909 packet.header.dst_cid(), +3910 ); +3911 } +3912 +3913 if self.is_handshaking() { +3914 if path_id != PathId::ZERO { +3915 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); +3916 return; +3917 } +3918 if network_path != self.path_data_mut(path_id).network_path { +3919 if let Some(hs) = self.state.as_handshake() { +3920 if hs.allow_server_migration { +3921 trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote"); +3922 self.path_data_mut(path_id).network_path = network_path; +3923 self.qlog.emit_tuple_assigned(path_id, network_path, now); +3924 } else { +3925 debug!("discarding packet with unexpected remote during handshake"); +3926 return; +3927 } +3928 } else { +3929 debug!("discarding packet with unexpected remote during handshake"); +3930 return; +3931 } +3932 } +3933 } +3934 +3935 let was_closed = self.state.is_closed(); +3936 let was_drained = self.state.is_drained(); 3937 -3938 let was_closed = self.state.is_closed(); -3939 let was_drained = self.state.is_drained(); -3940 -3941 let decrypted = match packet { -3942 None => Err(None), -3943 Some(mut packet) => self -3944 .decrypt_packet(now, path_id, &mut packet) -3945 .map(move |number| (packet, number)), -3946 }; -3947 let result = match decrypted { -3948 _ if stateless_reset => { -3949 debug!("got stateless reset"); -3950 Err(ConnectionError::Reset) -3951 } -3952 Err(Some(e)) => { -3953 warn!("illegal packet: {}", e); -3954 Err(e.into()) -3955 } -3956 Err(None) => { -3957 debug!("failed to authenticate packet"); -3958 self.authentication_failures += 1; -3959 let integrity_limit = self -3960 .crypto_state -3961 .integrity_limit(self.highest_space) -3962 .unwrap(); -3963 if self.authentication_failures > integrity_limit { -3964 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) -3965 } else { -3966 return; -3967 } -3968 } -3969 Ok((packet, number)) => { -3970 qlog.header(&packet.header, number, path_id); -3971 let span = match number { -3972 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), -3973 None => trace_span!("recv", space = ?packet.header.space()), -3974 }; -3975 let _guard = span.enter(); -3976 -3977 let dedup = self.spaces[packet.header.space()] -3978 .path_space_mut(path_id) -3979 .map(|pns| &mut pns.dedup); -3980 if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { -3981 debug!("discarding possible duplicate packet"); -3982 self.qlog.emit_packet_received(qlog, now); -3983 return; -3984 } else if self.state.is_handshake() && packet.header.is_short() { -3985 // TODO: SHOULD buffer these to improve reordering tolerance. -3986 trace!("dropping short packet during handshake"); -3987 self.qlog.emit_packet_received(qlog, now); -3988 return; -3989 } else { -3990 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header -3991 && let Some(hs) = self.state.as_handshake() -3992 && self.side.is_server() -3993 && token != &hs.expected_token -3994 { -3995 // Clients must send the same retry token in every Initial. Initial -3996 // packets can be spoofed, so we discard rather than killing the -3997 // connection. -3998 warn!("discarding Initial with invalid retry token"); -3999 self.qlog.emit_packet_received(qlog, now); -4000 return; -4001 } -4002 -4003 if !self.state.is_closed() { -4004 let spin = match packet.header { -4005 Header::Short { spin, .. } => spin, -4006 _ => false, -4007 }; -4008 -4009 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { -4010 // Only the client is allowed to open paths -4011 self.ensure_path(path_id, network_path, now, number); -4012 } -4013 if self.paths.contains_key(&path_id) { -4014 self.on_packet_authenticated( -4015 now, -4016 packet.header.space(), -4017 path_id, -4018 ecn, -4019 number, -4020 spin, -4021 packet.header.is_1rtt(), -4022 ); -4023 } -4024 } -4025 -4026 let res = self.process_decrypted_packet( -4027 now, -4028 network_path, -4029 path_id, -4030 number, -4031 packet, -4032 &mut qlog, -4033 ); -4034 -4035 self.qlog.emit_packet_received(qlog, now); -4036 res -4037 } -4038 } -4039 }; -4040 -4041 // State transitions for error cases -4042 if let Err(conn_err) = result { -4043 match conn_err { -4044 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), -4045 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), -4046 ConnectionError::Reset -4047 | ConnectionError::TransportError(TransportError { -4048 code: TransportErrorCode::AEAD_LIMIT_REACHED, -4049 .. -4050 }) => { -4051 self.state.move_to_drained(Some(conn_err)); +3938 let decrypted = match packet { +3939 None => Err(None), +3940 Some(mut packet) => self +3941 .decrypt_packet(now, path_id, &mut packet) +3942 .map(move |number| (packet, number)), +3943 }; +3944 let result = match decrypted { +3945 _ if stateless_reset => { +3946 debug!("got stateless reset"); +3947 Err(ConnectionError::Reset) +3948 } +3949 Err(Some(e)) => { +3950 warn!("illegal packet: {}", e); +3951 Err(e.into()) +3952 } +3953 Err(None) => { +3954 debug!("failed to authenticate packet"); +3955 self.authentication_failures += 1; +3956 let integrity_limit = self +3957 .crypto_state +3958 .integrity_limit(self.highest_space) +3959 .unwrap(); +3960 if self.authentication_failures > integrity_limit { +3961 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) +3962 } else { +3963 return; +3964 } +3965 } +3966 Ok((packet, number)) => { +3967 qlog.header(&packet.header, number, path_id); +3968 let span = match number { +3969 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), +3970 None => trace_span!("recv", space = ?packet.header.space()), +3971 }; +3972 let _guard = span.enter(); +3973 +3974 let dedup = self.spaces[packet.header.space()] +3975 .path_space_mut(path_id) +3976 .map(|pns| &mut pns.dedup); +3977 if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { +3978 debug!("discarding possible duplicate packet"); +3979 self.qlog.emit_packet_received(qlog, now); +3980 return; +3981 } else if self.state.is_handshake() && packet.header.is_short() { +3982 // TODO: SHOULD buffer these to improve reordering tolerance. +3983 trace!("dropping short packet during handshake"); +3984 self.qlog.emit_packet_received(qlog, now); +3985 return; +3986 } else { +3987 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header +3988 && let Some(hs) = self.state.as_handshake() +3989 && self.side.is_server() +3990 && token != &hs.expected_token +3991 { +3992 // Clients must send the same retry token in every Initial. Initial +3993 // packets can be spoofed, so we discard rather than killing the +3994 // connection. +3995 warn!("discarding Initial with invalid retry token"); +3996 self.qlog.emit_packet_received(qlog, now); +3997 return; +3998 } +3999 +4000 if !self.state.is_closed() { +4001 let spin = match packet.header { +4002 Header::Short { spin, .. } => spin, +4003 _ => false, +4004 }; +4005 +4006 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { +4007 // Only the client is allowed to open paths +4008 self.ensure_path(path_id, network_path, now, number); +4009 } +4010 if self.paths.contains_key(&path_id) { +4011 self.on_packet_authenticated( +4012 now, +4013 packet.header.space(), +4014 path_id, +4015 ecn, +4016 number, +4017 spin, +4018 packet.header.is_1rtt(), +4019 ); +4020 } +4021 } +4022 +4023 let res = self.process_decrypted_packet( +4024 now, +4025 network_path, +4026 path_id, +4027 number, +4028 packet, +4029 &mut qlog, +4030 ); +4031 +4032 self.qlog.emit_packet_received(qlog, now); +4033 res +4034 } +4035 } +4036 }; +4037 +4038 // State transitions for error cases +4039 if let Err(conn_err) = result { +4040 match conn_err { +4041 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), +4042 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), +4043 ConnectionError::Reset +4044 | ConnectionError::TransportError(TransportError { +4045 code: TransportErrorCode::AEAD_LIMIT_REACHED, +4046 .. +4047 }) => { +4048 self.state.move_to_drained(Some(conn_err)); +4049 } +4050 ConnectionError::TimedOut => { +4051 unreachable!("timeouts aren't generated by packet processing"); 4052 } -4053 ConnectionError::TimedOut => { -4054 unreachable!("timeouts aren't generated by packet processing"); -4055 } -4056 ConnectionError::TransportError(err) => { -4057 debug!("closing connection due to transport error: {}", err); -4058 self.state.move_to_closed(err); +4053 ConnectionError::TransportError(err) => { +4054 debug!("closing connection due to transport error: {}", err); +4055 self.state.move_to_closed(err); +4056 } +4057 ConnectionError::VersionMismatch => { +4058 self.state.move_to_draining(Some(conn_err)); 4059 } -4060 ConnectionError::VersionMismatch => { -4061 self.state.move_to_draining(Some(conn_err)); +4060 ConnectionError::LocallyClosed => { +4061 unreachable!("LocallyClosed isn't generated by packet processing"); 4062 } -4063 ConnectionError::LocallyClosed => { -4064 unreachable!("LocallyClosed isn't generated by packet processing"); +4063 ConnectionError::CidsExhausted => { +4064 unreachable!("CidsExhausted isn't generated by packet processing"); 4065 } -4066 ConnectionError::CidsExhausted => { -4067 unreachable!("CidsExhausted isn't generated by packet processing"); -4068 } -4069 }; -4070 } -4071 -4072 if !was_closed && self.state.is_closed() { -4073 self.close_common(); -4074 if !self.state.is_drained() { -4075 self.set_close_timer(now); -4076 } -4077 } -4078 if !was_drained && self.state.is_drained() { -4079 self.endpoint_events.push_back(EndpointEventInner::Drained); -4080 // Close timer may have been started previously, e.g. if we sent a close and got a -4081 // stateless reset in response -4082 self.timers -4083 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); -4084 } -4085 -4086 // Transmit CONNECTION_CLOSE if necessary. -4087 // -4088 // If we received a valid packet and we are in the closed state we should respond -4089 // with a CONNECTION_CLOSE frame. -4090 // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but -4091 // that does not yet happen. This is triggered by each received packet. -4092 if matches!(self.state.as_type(), StateType::Closed) { -4093 // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7 -4094 // -4095 // While in the closing state we must either: -4096 // - discard packets coming from an un-validated remote OR -4097 // - ensure we do not send more than 3 times the received data -4098 // -4099 // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer -4100 // who was (involuntary) migrated just at the time we initiated immediate -4101 // close. It is a lot more work though. So while we would like to do this for -4102 // now we only do 1. -4103 // -4104 // Another shortcoming of the current implementation is that when we have a -4105 // previous PathData which is validated and the remote matches that path, we -4106 // should schedule CONNECTION_CLOSE on that path. However currently we can not -4107 // schedule such a packet. We should also fix this some day. This makes us -4108 // vulnerable to an attacker faking a migration at the right time and then we'd -4109 // be unable to send the CONNECTION_CLOSE to the real remote. -4110 if self -4111 .paths -4112 .get(&path_id) -4113 .map(|p| p.data.validated && p.data.network_path == network_path) -4114 .unwrap_or(false) -4115 { -4116 self.connection_close_pending = true; -4117 } -4118 } -4119 } -4120 -4121 fn process_decrypted_packet( -4122 &mut self, -4123 now: Instant, -4124 network_path: FourTuple, -4125 path_id: PathId, -4126 number: Option<u64>, -4127 packet: Packet, -4128 qlog: &mut QlogRecvPacket, -4129 ) -> Result<(), ConnectionError> { -4130 if !self.paths.contains_key(&path_id) { -4131 // There is a chance this is a server side, first (for this path) packet, which would -4132 // be a protocol violation. It's more likely, however, that this is a packet of a -4133 // pruned path -4134 trace!(%path_id, ?number, "discarding packet for unknown path"); -4135 return Ok(()); -4136 } -4137 let state = match self.state.as_type() { -4138 StateType::Established => { -4139 match packet.header.space() { -4140 SpaceKind::Data => self.process_payload( -4141 now, -4142 network_path, -4143 path_id, -4144 number.unwrap(), -4145 packet, -4146 qlog, -4147 )?, -4148 _ if packet.header.has_frames() => { -4149 self.process_early_payload(now, path_id, packet, qlog)? -4150 } -4151 _ => { -4152 trace!("discarding unexpected pre-handshake packet"); -4153 } -4154 } -4155 return Ok(()); -4156 } -4157 StateType::Closed => { -4158 for result in frame::Iter::new(packet.payload.freeze())? { -4159 let frame = match result { -4160 Ok(frame) => frame, -4161 Err(err) => { -4162 debug!("frame decoding error: {err:?}"); -4163 continue; -4164 } -4165 }; -4166 qlog.frame(&frame); -4167 -4168 if let Frame::Padding = frame { -4169 continue; -4170 }; -4171 -4172 self.stats.frame_rx.record(frame.ty()); -4173 -4174 if let Frame::Close(_error) = frame { -4175 self.state.move_to_draining(None); -4176 break; -4177 } -4178 } -4179 return Ok(()); -4180 } -4181 StateType::Draining | StateType::Drained => return Ok(()), -4182 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), -4183 }; -4184 -4185 match packet.header { -4186 Header::Retry { -4187 src_cid: remote_cid, -4188 .. -4189 } => { -4190 debug_assert_eq!(path_id, PathId::ZERO); -4191 if self.side.is_server() { -4192 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); -4193 } -4194 -4195 let is_valid_retry = self -4196 .remote_cids -4197 .get(&path_id) -4198 .map(|cids| cids.active()) -4199 .map(|orig_dst_cid| { -4200 self.crypto_state.session.is_valid_retry( -4201 orig_dst_cid, -4202 &packet.header_data, -4203 &packet.payload, -4204 ) -4205 }) -4206 .unwrap_or_default(); -4207 if self.total_authed_packets > 1 -4208 || packet.payload.len() <= 16 // token + 16 byte tag -4209 || !is_valid_retry -4210 { -4211 trace!("discarding invalid Retry"); -4212 // - After the client has received and processed an Initial or Retry -4213 // packet from the server, it MUST discard any subsequent Retry -4214 // packets that it receives. -4215 // - A client MUST discard a Retry packet with a zero-length Retry Token -4216 // field. -4217 // - Clients MUST discard Retry packets that have a Retry Integrity Tag -4218 // that cannot be validated -4219 return Ok(()); -4220 } -4221 -4222 trace!("retrying with CID {}", remote_cid); -4223 let client_hello = state.client_hello.take().unwrap(); -4224 self.retry_src_cid = Some(remote_cid); -4225 self.remote_cids -4226 .get_mut(&path_id) -4227 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") -4228 .update_initial_cid(remote_cid); -4229 self.remote_handshake_cid = remote_cid; -4230 -4231 let space = &mut self.spaces[SpaceId::Initial]; -4232 if let Some(info) = space.for_path(PathId::ZERO).take(0) { -4233 self.on_packet_acked(now, PathId::ZERO, info); -4234 }; -4235 -4236 self.discard_space(now, SpaceKind::Initial); // Make sure we clean up after -4237 // any retransmitted Initials -4238 let crypto_space = &mut self.crypto_state.spaces[SpaceKind::Initial]; -4239 crypto_space.keys = Some( -4240 self.crypto_state -4241 .session -4242 .initial_keys(remote_cid, self.side.side()), -4243 ); -4244 crypto_space.crypto_offset = client_hello.len() as u64; -4245 -4246 let next_pn = self.spaces[SpaceId::Initial] -4247 .for_path(path_id) -4248 .next_packet_number; -4249 self.spaces[SpaceId::Initial] = { -4250 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); -4251 space.for_path(path_id).next_packet_number = next_pn; -4252 space.pending.crypto.push_back(frame::Crypto { -4253 offset: 0, -4254 data: client_hello, -4255 }); -4256 space -4257 }; -4258 -4259 // Retransmit all 0-RTT data -4260 let zero_rtt = mem::take( -4261 &mut self.spaces[SpaceId::Data] -4262 .for_path(PathId::ZERO) -4263 .sent_packets, -4264 ); -4265 for (_, info) in zero_rtt.into_iter() { -4266 self.paths -4267 .get_mut(&PathId::ZERO) -4268 .unwrap() -4269 .remove_in_flight(&info); -4270 self.spaces[SpaceId::Data].pending |= info.retransmits; -4271 } -4272 self.streams.retransmit_all_for_0rtt(); -4273 -4274 let token_len = packet.payload.len() - 16; -4275 let ConnectionSide::Client { ref mut token, .. } = self.side else { -4276 unreachable!("we already short-circuited if we're server"); -4277 }; -4278 *token = packet.payload.freeze().split_to(token_len); -4279 -4280 self.state = State::handshake(state::Handshake { -4281 expected_token: Bytes::new(), -4282 remote_cid_set: false, -4283 client_hello: None, -4284 allow_server_migration: true, -4285 }); -4286 Ok(()) -4287 } -4288 Header::Long { -4289 ty: LongType::Handshake, -4290 src_cid: remote_cid, -4291 dst_cid: local_cid, -4292 .. -4293 } => { -4294 debug_assert_eq!(path_id, PathId::ZERO); -4295 if remote_cid != self.remote_handshake_cid { -4296 debug!( -4297 "discarding packet with mismatched remote CID: {} != {}", -4298 self.remote_handshake_cid, remote_cid -4299 ); -4300 return Ok(()); -4301 } -4302 self.on_path_validated(path_id); -4303 -4304 self.process_early_payload(now, path_id, packet, qlog)?; -4305 if self.state.is_closed() { -4306 return Ok(()); -4307 } -4308 -4309 if self.crypto_state.session.is_handshaking() { -4310 trace!("handshake ongoing"); -4311 return Ok(()); -4312 } -4313 -4314 if self.side.is_client() { -4315 // Client-only because server params were set from the client's Initial -4316 let params = self -4317 .crypto_state -4318 .session -4319 .transport_parameters()? -4320 .ok_or_else(|| { -4321 TransportError::new( -4322 TransportErrorCode::crypto(0x6d), -4323 "transport parameters missing".to_owned(), -4324 ) -4325 })?; -4326 -4327 if self.has_0rtt() { -4328 if !self.crypto_state.session.early_data_accepted().unwrap() { -4329 debug_assert!(self.side.is_client()); -4330 debug!("0-RTT rejected"); -4331 self.crypto_state.accepted_0rtt = false; -4332 self.streams.zero_rtt_rejected(); +4066 }; +4067 } +4068 +4069 if !was_closed && self.state.is_closed() { +4070 self.close_common(); +4071 if !self.state.is_drained() { +4072 self.set_close_timer(now); +4073 } +4074 } +4075 if !was_drained && self.state.is_drained() { +4076 self.endpoint_events.push_back(EndpointEventInner::Drained); +4077 // Close timer may have been started previously, e.g. if we sent a close and got a +4078 // stateless reset in response +4079 self.timers +4080 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); +4081 } +4082 +4083 // Transmit CONNECTION_CLOSE if necessary. +4084 // +4085 // If we received a valid packet and we are in the closed state we should respond +4086 // with a CONNECTION_CLOSE frame. +4087 // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but +4088 // that does not yet happen. This is triggered by each received packet. +4089 if matches!(self.state.as_type(), StateType::Closed) { +4090 // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7 +4091 // +4092 // While in the closing state we must either: +4093 // - discard packets coming from an un-validated remote OR +4094 // - ensure we do not send more than 3 times the received data +4095 // +4096 // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer +4097 // who was (involuntary) migrated just at the time we initiated immediate +4098 // close. It is a lot more work though. So while we would like to do this for +4099 // now we only do 1. +4100 // +4101 // Another shortcoming of the current implementation is that when we have a +4102 // previous PathData which is validated and the remote matches that path, we +4103 // should schedule CONNECTION_CLOSE on that path. However currently we can not +4104 // schedule such a packet. We should also fix this some day. This makes us +4105 // vulnerable to an attacker faking a migration at the right time and then we'd +4106 // be unable to send the CONNECTION_CLOSE to the real remote. +4107 if self +4108 .paths +4109 .get(&path_id) +4110 .map(|p| p.data.validated && p.data.network_path == network_path) +4111 .unwrap_or(false) +4112 { +4113 self.connection_close_pending = true; +4114 } +4115 } +4116 } +4117 +4118 fn process_decrypted_packet( +4119 &mut self, +4120 now: Instant, +4121 network_path: FourTuple, +4122 path_id: PathId, +4123 number: Option<u64>, +4124 packet: Packet, +4125 qlog: &mut QlogRecvPacket, +4126 ) -> Result<(), ConnectionError> { +4127 if !self.paths.contains_key(&path_id) { +4128 // There is a chance this is a server side, first (for this path) packet, which would +4129 // be a protocol violation. It's more likely, however, that this is a packet of a +4130 // pruned path +4131 trace!(%path_id, ?number, "discarding packet for unknown path"); +4132 return Ok(()); +4133 } +4134 let state = match self.state.as_type() { +4135 StateType::Established => { +4136 match packet.header.space() { +4137 SpaceKind::Data => self.process_payload( +4138 now, +4139 network_path, +4140 path_id, +4141 number.unwrap(), +4142 packet, +4143 qlog, +4144 )?, +4145 _ if packet.header.has_frames() => { +4146 self.process_early_payload(now, path_id, packet, qlog)? +4147 } +4148 _ => { +4149 trace!("discarding unexpected pre-handshake packet"); +4150 } +4151 } +4152 return Ok(()); +4153 } +4154 StateType::Closed => { +4155 for result in frame::Iter::new(packet.payload.freeze())? { +4156 let frame = match result { +4157 Ok(frame) => frame, +4158 Err(err) => { +4159 debug!("frame decoding error: {err:?}"); +4160 continue; +4161 } +4162 }; +4163 qlog.frame(&frame); +4164 +4165 if let Frame::Padding = frame { +4166 continue; +4167 }; +4168 +4169 self.stats.frame_rx.record(frame.ty()); +4170 +4171 if let Frame::Close(_error) = frame { +4172 self.state.move_to_draining(None); +4173 break; +4174 } +4175 } +4176 return Ok(()); +4177 } +4178 StateType::Draining | StateType::Drained => return Ok(()), +4179 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), +4180 }; +4181 +4182 match packet.header { +4183 Header::Retry { +4184 src_cid: remote_cid, +4185 .. +4186 } => { +4187 debug_assert_eq!(path_id, PathId::ZERO); +4188 if self.side.is_server() { +4189 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); +4190 } +4191 +4192 let is_valid_retry = self +4193 .remote_cids +4194 .get(&path_id) +4195 .map(|cids| cids.active()) +4196 .map(|orig_dst_cid| { +4197 self.crypto_state.session.is_valid_retry( +4198 orig_dst_cid, +4199 &packet.header_data, +4200 &packet.payload, +4201 ) +4202 }) +4203 .unwrap_or_default(); +4204 if self.total_authed_packets > 1 +4205 || packet.payload.len() <= 16 // token + 16 byte tag +4206 || !is_valid_retry +4207 { +4208 trace!("discarding invalid Retry"); +4209 // - After the client has received and processed an Initial or Retry +4210 // packet from the server, it MUST discard any subsequent Retry +4211 // packets that it receives. +4212 // - A client MUST discard a Retry packet with a zero-length Retry Token +4213 // field. +4214 // - Clients MUST discard Retry packets that have a Retry Integrity Tag +4215 // that cannot be validated +4216 return Ok(()); +4217 } +4218 +4219 trace!("retrying with CID {}", remote_cid); +4220 let client_hello = state.client_hello.take().unwrap(); +4221 self.retry_src_cid = Some(remote_cid); +4222 self.remote_cids +4223 .get_mut(&path_id) +4224 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") +4225 .update_initial_cid(remote_cid); +4226 self.remote_handshake_cid = remote_cid; +4227 +4228 let space = &mut self.spaces[SpaceId::Initial]; +4229 if let Some(info) = space.for_path(PathId::ZERO).take(0) { +4230 self.on_packet_acked(now, PathId::ZERO, info); +4231 }; +4232 +4233 self.discard_space(now, SpaceKind::Initial); // Make sure we clean up after +4234 // any retransmitted Initials +4235 let crypto_space = &mut self.crypto_state.spaces[SpaceKind::Initial]; +4236 crypto_space.keys = Some( +4237 self.crypto_state +4238 .session +4239 .initial_keys(remote_cid, self.side.side()), +4240 ); +4241 crypto_space.crypto_offset = client_hello.len() as u64; +4242 +4243 let next_pn = self.spaces[SpaceId::Initial] +4244 .for_path(path_id) +4245 .next_packet_number; +4246 self.spaces[SpaceId::Initial] = { +4247 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); +4248 space.for_path(path_id).next_packet_number = next_pn; +4249 space.pending.crypto.push_back(frame::Crypto { +4250 offset: 0, +4251 data: client_hello, +4252 }); +4253 space +4254 }; +4255 +4256 // Retransmit all 0-RTT data +4257 let zero_rtt = mem::take( +4258 &mut self.spaces[SpaceId::Data] +4259 .for_path(PathId::ZERO) +4260 .sent_packets, +4261 ); +4262 for (_, info) in zero_rtt.into_iter() { +4263 self.paths +4264 .get_mut(&PathId::ZERO) +4265 .unwrap() +4266 .remove_in_flight(&info); +4267 self.spaces[SpaceId::Data].pending |= info.retransmits; +4268 } +4269 self.streams.retransmit_all_for_0rtt(); +4270 +4271 let token_len = packet.payload.len() - 16; +4272 let ConnectionSide::Client { ref mut token, .. } = self.side else { +4273 unreachable!("we already short-circuited if we're server"); +4274 }; +4275 *token = packet.payload.freeze().split_to(token_len); +4276 +4277 self.state = State::handshake(state::Handshake { +4278 expected_token: Bytes::new(), +4279 remote_cid_set: false, +4280 client_hello: None, +4281 allow_server_migration: true, +4282 }); +4283 Ok(()) +4284 } +4285 Header::Long { +4286 ty: LongType::Handshake, +4287 src_cid: remote_cid, +4288 dst_cid: local_cid, +4289 .. +4290 } => { +4291 debug_assert_eq!(path_id, PathId::ZERO); +4292 if remote_cid != self.remote_handshake_cid { +4293 debug!( +4294 "discarding packet with mismatched remote CID: {} != {}", +4295 self.remote_handshake_cid, remote_cid +4296 ); +4297 return Ok(()); +4298 } +4299 self.on_path_validated(path_id); +4300 +4301 self.process_early_payload(now, path_id, packet, qlog)?; +4302 if self.state.is_closed() { +4303 return Ok(()); +4304 } +4305 +4306 if self.crypto_state.session.is_handshaking() { +4307 trace!("handshake ongoing"); +4308 return Ok(()); +4309 } +4310 +4311 if self.side.is_client() { +4312 // Client-only because server params were set from the client's Initial +4313 let params = self +4314 .crypto_state +4315 .session +4316 .transport_parameters()? +4317 .ok_or_else(|| { +4318 TransportError::new( +4319 TransportErrorCode::crypto(0x6d), +4320 "transport parameters missing".to_owned(), +4321 ) +4322 })?; +4323 +4324 if self.has_0rtt() { +4325 if !self.crypto_state.session.early_data_accepted().unwrap() { +4326 debug_assert!(self.side.is_client()); +4327 debug!("0-RTT rejected"); +4328 self.crypto_state.accepted_0rtt = false; +4329 self.streams.zero_rtt_rejected(); +4330 +4331 // Discard already-queued frames +4332 self.spaces[SpaceId::Data].pending = Retransmits::default(); 4333 -4334 // Discard already-queued frames -4335 self.spaces[SpaceId::Data].pending = Retransmits::default(); -4336 -4337 // Discard 0-RTT packets -4338 let sent_packets = mem::take( -4339 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, -4340 ); -4341 for (_, packet) in sent_packets.into_iter() { -4342 self.paths -4343 .get_mut(&path_id) -4344 .unwrap() -4345 .remove_in_flight(&packet); -4346 } -4347 } else { -4348 self.crypto_state.accepted_0rtt = true; -4349 params.validate_resumption_from(&self.peer_params)?; -4350 } -4351 } -4352 if let Some(token) = params.stateless_reset_token { -4353 // TODO(matheus23): Reset token for a remote, or for a 4-tuple? -4354 let remote = self.path_data(path_id).network_path.remote; -4355 self.endpoint_events -4356 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); -4357 } -4358 self.handle_peer_params(params, local_cid, remote_cid, now)?; -4359 self.issue_first_cids(now); -4360 } else { -4361 // Server-only -4362 self.spaces[SpaceId::Data].pending.handshake_done = true; -4363 self.discard_space(now, SpaceKind::Handshake); -4364 self.events.push_back(Event::HandshakeConfirmed); -4365 trace!("handshake confirmed"); -4366 } -4367 -4368 self.events.push_back(Event::Connected); -4369 self.state.move_to_established(); -4370 trace!("established"); -4371 -4372 // Multipath can only be enabled after the state has reached Established. -4373 // So this can not happen any earlier. -4374 self.issue_first_path_cids(now); -4375 Ok(()) -4376 } -4377 Header::Initial(InitialHeader { -4378 src_cid: remote_cid, -4379 dst_cid: local_cid, -4380 .. -4381 }) => { -4382 debug_assert_eq!(path_id, PathId::ZERO); -4383 if !state.remote_cid_set { -4384 trace!("switching remote CID to {}", remote_cid); -4385 let mut state = state.clone(); -4386 self.remote_cids -4387 .get_mut(&path_id) -4388 .expect("PathId::ZERO not yet abandoned") -4389 .update_initial_cid(remote_cid); -4390 self.remote_handshake_cid = remote_cid; -4391 self.original_remote_cid = remote_cid; -4392 state.remote_cid_set = true; -4393 self.state.move_to_handshake(state); -4394 } else if remote_cid != self.remote_handshake_cid { -4395 debug!( -4396 "discarding packet with mismatched remote CID: {} != {}", -4397 self.remote_handshake_cid, remote_cid -4398 ); -4399 return Ok(()); -4400 } +4334 // Discard 0-RTT packets +4335 let sent_packets = mem::take( +4336 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, +4337 ); +4338 for (_, packet) in sent_packets.into_iter() { +4339 self.paths +4340 .get_mut(&path_id) +4341 .unwrap() +4342 .remove_in_flight(&packet); +4343 } +4344 } else { +4345 self.crypto_state.accepted_0rtt = true; +4346 params.validate_resumption_from(&self.peer_params)?; +4347 } +4348 } +4349 if let Some(token) = params.stateless_reset_token { +4350 // TODO(matheus23): Reset token for a remote, or for a 4-tuple? +4351 let remote = self.path_data(path_id).network_path.remote; +4352 self.endpoint_events +4353 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); +4354 } +4355 self.handle_peer_params(params, local_cid, remote_cid, now)?; +4356 self.issue_first_cids(now); +4357 } else { +4358 // Server-only +4359 self.spaces[SpaceId::Data].pending.handshake_done = true; +4360 self.discard_space(now, SpaceKind::Handshake); +4361 self.events.push_back(Event::HandshakeConfirmed); +4362 trace!("handshake confirmed"); +4363 } +4364 +4365 self.events.push_back(Event::Connected); +4366 self.state.move_to_established(); +4367 trace!("established"); +4368 +4369 // Multipath can only be enabled after the state has reached Established. +4370 // So this can not happen any earlier. +4371 self.issue_first_path_cids(now); +4372 Ok(()) +4373 } +4374 Header::Initial(InitialHeader { +4375 src_cid: remote_cid, +4376 dst_cid: local_cid, +4377 .. +4378 }) => { +4379 debug_assert_eq!(path_id, PathId::ZERO); +4380 if !state.remote_cid_set { +4381 trace!("switching remote CID to {}", remote_cid); +4382 let mut state = state.clone(); +4383 self.remote_cids +4384 .get_mut(&path_id) +4385 .expect("PathId::ZERO not yet abandoned") +4386 .update_initial_cid(remote_cid); +4387 self.remote_handshake_cid = remote_cid; +4388 self.original_remote_cid = remote_cid; +4389 state.remote_cid_set = true; +4390 self.state.move_to_handshake(state); +4391 } else if remote_cid != self.remote_handshake_cid { +4392 debug!( +4393 "discarding packet with mismatched remote CID: {} != {}", +4394 self.remote_handshake_cid, remote_cid +4395 ); +4396 return Ok(()); +4397 } +4398 +4399 let starting_space = self.highest_space; +4400 self.process_early_payload(now, path_id, packet, qlog)?; 4401 -4402 let starting_space = self.highest_space; -4403 self.process_early_payload(now, path_id, packet, qlog)?; -4404 -4405 if self.side.is_server() -4406 && starting_space == SpaceKind::Initial -4407 && self.highest_space != SpaceKind::Initial -4408 { -4409 let params = self -4410 .crypto_state -4411 .session -4412 .transport_parameters()? -4413 .ok_or_else(|| { -4414 TransportError::new( -4415 TransportErrorCode::crypto(0x6d), -4416 "transport parameters missing".to_owned(), -4417 ) -4418 })?; -4419 self.handle_peer_params(params, local_cid, remote_cid, now)?; -4420 self.issue_first_cids(now); -4421 self.init_0rtt(now); -4422 } -4423 Ok(()) -4424 } -4425 Header::Long { -4426 ty: LongType::ZeroRtt, -4427 .. -4428 } => { -4429 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; -4430 Ok(()) -4431 } -4432 Header::VersionNegotiate { .. } => { -4433 if self.total_authed_packets > 1 { -4434 return Ok(()); -4435 } -4436 let supported = packet -4437 .payload -4438 .chunks(4) -4439 .any(|x| match <[u8; 4]>::try_from(x) { -4440 Ok(version) => self.version == u32::from_be_bytes(version), -4441 Err(_) => false, -4442 }); -4443 if supported { -4444 return Ok(()); -4445 } -4446 debug!("remote doesn't support our version"); -4447 Err(ConnectionError::VersionMismatch) -4448 } -4449 Header::Short { .. } => unreachable!( -4450 "short packets received during handshake are discarded in handle_packet" -4451 ), -4452 } -4453 } -4454 -4455 /// Process an Initial or Handshake packet payload -4456 fn process_early_payload( -4457 &mut self, -4458 now: Instant, -4459 path_id: PathId, -4460 packet: Packet, -4461 #[allow(unused)] qlog: &mut QlogRecvPacket, -4462 ) -> Result<(), TransportError> { -4463 debug_assert_ne!(packet.header.space(), SpaceKind::Data); -4464 debug_assert_eq!(path_id, PathId::ZERO); -4465 let payload_len = packet.payload.len(); -4466 let mut ack_eliciting = false; -4467 for result in frame::Iter::new(packet.payload.freeze())? { -4468 let frame = result?; -4469 qlog.frame(&frame); -4470 let span = match frame { -4471 Frame::Padding => continue, -4472 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), -4473 }; -4474 -4475 self.stats.frame_rx.record(frame.ty()); +4402 if self.side.is_server() +4403 && starting_space == SpaceKind::Initial +4404 && self.highest_space != SpaceKind::Initial +4405 { +4406 let params = self +4407 .crypto_state +4408 .session +4409 .transport_parameters()? +4410 .ok_or_else(|| { +4411 TransportError::new( +4412 TransportErrorCode::crypto(0x6d), +4413 "transport parameters missing".to_owned(), +4414 ) +4415 })?; +4416 self.handle_peer_params(params, local_cid, remote_cid, now)?; +4417 self.issue_first_cids(now); +4418 self.init_0rtt(now); +4419 } +4420 Ok(()) +4421 } +4422 Header::Long { +4423 ty: LongType::ZeroRtt, +4424 .. +4425 } => { +4426 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; +4427 Ok(()) +4428 } +4429 Header::VersionNegotiate { .. } => { +4430 if self.total_authed_packets > 1 { +4431 return Ok(()); +4432 } +4433 let supported = packet +4434 .payload +4435 .chunks(4) +4436 .any(|x| match <[u8; 4]>::try_from(x) { +4437 Ok(version) => self.version == u32::from_be_bytes(version), +4438 Err(_) => false, +4439 }); +4440 if supported { +4441 return Ok(()); +4442 } +4443 debug!("remote doesn't support our version"); +4444 Err(ConnectionError::VersionMismatch) +4445 } +4446 Header::Short { .. } => unreachable!( +4447 "short packets received during handshake are discarded in handle_packet" +4448 ), +4449 } +4450 } +4451 +4452 /// Process an Initial or Handshake packet payload +4453 fn process_early_payload( +4454 &mut self, +4455 now: Instant, +4456 path_id: PathId, +4457 packet: Packet, +4458 #[allow(unused)] qlog: &mut QlogRecvPacket, +4459 ) -> Result<(), TransportError> { +4460 debug_assert_ne!(packet.header.space(), SpaceKind::Data); +4461 debug_assert_eq!(path_id, PathId::ZERO); +4462 let payload_len = packet.payload.len(); +4463 let mut ack_eliciting = false; +4464 for result in frame::Iter::new(packet.payload.freeze())? { +4465 let frame = result?; +4466 qlog.frame(&frame); +4467 let span = match frame { +4468 Frame::Padding => continue, +4469 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), +4470 }; +4471 +4472 self.stats.frame_rx.record(frame.ty()); +4473 +4474 let _guard = span.as_ref().map(|x| x.enter()); +4475 ack_eliciting |= frame.is_ack_eliciting(); 4476 -4477 let _guard = span.as_ref().map(|x| x.enter()); -4478 ack_eliciting |= frame.is_ack_eliciting(); -4479 -4480 // Process frames -4481 if frame.is_1rtt() && packet.header.space() != SpaceKind::Data { -4482 return Err(TransportError::PROTOCOL_VIOLATION( -4483 "illegal frame type in handshake", -4484 )); -4485 } -4486 -4487 match frame { -4488 Frame::Padding | Frame::Ping => {} -4489 Frame::Crypto(frame) => { -4490 self.read_crypto(packet.header.space().into(), &frame, payload_len)?; +4477 // Process frames +4478 if frame.is_1rtt() && packet.header.space() != SpaceKind::Data { +4479 return Err(TransportError::PROTOCOL_VIOLATION( +4480 "illegal frame type in handshake", +4481 )); +4482 } +4483 +4484 match frame { +4485 Frame::Padding | Frame::Ping => {} +4486 Frame::Crypto(frame) => { +4487 self.read_crypto(packet.header.space().into(), &frame, payload_len)?; +4488 } +4489 Frame::Ack(ack) => { +4490 self.on_ack_received(now, packet.header.space().into(), ack)?; 4491 } -4492 Frame::Ack(ack) => { -4493 self.on_ack_received(now, packet.header.space().into(), ack)?; -4494 } -4495 Frame::PathAck(ack) => { -4496 span.as_ref() -4497 .map(|span| span.record("path", tracing::field::display(&ack.path_id))); -4498 self.on_path_ack_received(now, packet.header.space().into(), ack)?; -4499 } -4500 Frame::Close(reason) => { -4501 self.state.move_to_draining(Some(reason.into())); -4502 return Ok(()); -4503 } -4504 _ => { -4505 let mut err = -4506 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); -4507 err.frame = frame::MaybeFrame::Known(frame.ty()); -4508 return Err(err); -4509 } -4510 } -4511 } -4512 -4513 if ack_eliciting { -4514 // In the initial and handshake spaces, ACKs must be sent immediately -4515 self.spaces[packet.header.space()] -4516 .for_path(path_id) -4517 .pending_acks -4518 .set_immediate_ack_required(); -4519 } -4520 -4521 self.write_crypto(); -4522 Ok(()) -4523 } -4524 -4525 /// Processes the packet payload, always in the data space. -4526 fn process_payload( -4527 &mut self, -4528 now: Instant, -4529 network_path: FourTuple, -4530 path_id: PathId, -4531 number: u64, -4532 packet: Packet, -4533 #[allow(unused)] qlog: &mut QlogRecvPacket, -4534 ) -> Result<(), TransportError> { -4535 let is_multipath_negotiated = self.is_multipath_negotiated(); -4536 let payload = packet.payload.freeze(); -4537 let mut is_probing_packet = true; -4538 let mut close = None; -4539 let payload_len = payload.len(); -4540 let mut ack_eliciting = false; -4541 // if this packet triggers a path migration and includes a observed address frame, it's -4542 // stored here -4543 let mut migration_observed_addr = None; -4544 for result in frame::Iter::new(payload)? { -4545 let frame = result?; -4546 qlog.frame(&frame); -4547 let span = match frame { -4548 Frame::Padding => continue, -4549 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), -4550 }; -4551 -4552 self.stats.frame_rx.record(frame.ty()); -4553 // Crypto, Stream and Datagram frames are special cased in order no pollute -4554 // the log with payload data -4555 match &frame { -4556 Frame::Crypto(f) => { -4557 trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO"); +4492 Frame::PathAck(ack) => { +4493 span.as_ref() +4494 .map(|span| span.record("path", tracing::field::display(&ack.path_id))); +4495 self.on_path_ack_received(now, packet.header.space().into(), ack)?; +4496 } +4497 Frame::Close(reason) => { +4498 self.state.move_to_draining(Some(reason.into())); +4499 return Ok(()); +4500 } +4501 _ => { +4502 let mut err = +4503 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); +4504 err.frame = frame::MaybeFrame::Known(frame.ty()); +4505 return Err(err); +4506 } +4507 } +4508 } +4509 +4510 if ack_eliciting { +4511 // In the initial and handshake spaces, ACKs must be sent immediately +4512 self.spaces[packet.header.space()] +4513 .for_path(path_id) +4514 .pending_acks +4515 .set_immediate_ack_required(); +4516 } +4517 +4518 self.write_crypto(); +4519 Ok(()) +4520 } +4521 +4522 /// Processes the packet payload, always in the data space. +4523 fn process_payload( +4524 &mut self, +4525 now: Instant, +4526 network_path: FourTuple, +4527 path_id: PathId, +4528 number: u64, +4529 packet: Packet, +4530 #[allow(unused)] qlog: &mut QlogRecvPacket, +4531 ) -> Result<(), TransportError> { +4532 let is_multipath_negotiated = self.is_multipath_negotiated(); +4533 let payload = packet.payload.freeze(); +4534 let mut is_probing_packet = true; +4535 let mut close = None; +4536 let payload_len = payload.len(); +4537 let mut ack_eliciting = false; +4538 // if this packet triggers a path migration and includes a observed address frame, it's +4539 // stored here +4540 let mut migration_observed_addr = None; +4541 for result in frame::Iter::new(payload)? { +4542 let frame = result?; +4543 qlog.frame(&frame); +4544 let span = match frame { +4545 Frame::Padding => continue, +4546 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), +4547 }; +4548 +4549 self.stats.frame_rx.record(frame.ty()); +4550 // Crypto, Stream and Datagram frames are special cased in order no pollute +4551 // the log with payload data +4552 match &frame { +4553 Frame::Crypto(f) => { +4554 trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO"); +4555 } +4556 Frame::Stream(f) => { +4557 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM"); 4558 } -4559 Frame::Stream(f) => { -4560 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM"); +4559 Frame::Datagram(f) => { +4560 trace!(len = f.data.len(), "got frame DATAGRAM"); 4561 } -4562 Frame::Datagram(f) => { -4563 trace!(len = f.data.len(), "got frame DATAGRAM"); +4562 f => { +4563 trace!("got frame {f}"); 4564 } -4565 f => { -4566 trace!("got frame {f}"); -4567 } -4568 } -4569 -4570 let _guard = span.enter(); -4571 if packet.header.is_0rtt() { -4572 match frame { -4573 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { -4574 return Err(TransportError::PROTOCOL_VIOLATION( -4575 "illegal frame type in 0-RTT", -4576 )); -4577 } -4578 _ => { -4579 if frame.is_1rtt() { -4580 return Err(TransportError::PROTOCOL_VIOLATION( -4581 "illegal frame type in 0-RTT", -4582 )); -4583 } -4584 } -4585 } -4586 } -4587 ack_eliciting |= frame.is_ack_eliciting(); -4588 -4589 // Check whether this could be a probing packet -4590 match frame { -4591 Frame::Padding -4592 | Frame::PathChallenge(_) -4593 | Frame::PathResponse(_) -4594 | Frame::NewConnectionId(_) -4595 | Frame::ObservedAddr(_) => {} -4596 _ => { -4597 is_probing_packet = false; -4598 } -4599 } -4600 -4601 match frame { -4602 Frame::Crypto(frame) => { -4603 self.read_crypto(SpaceId::Data, &frame, payload_len)?; -4604 } -4605 Frame::Stream(frame) => { -4606 if self.streams.received(frame, payload_len)?.should_transmit() { -4607 self.spaces[SpaceId::Data].pending.max_data = true; -4608 } +4565 } +4566 +4567 let _guard = span.enter(); +4568 if packet.header.is_0rtt() { +4569 match frame { +4570 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { +4571 return Err(TransportError::PROTOCOL_VIOLATION( +4572 "illegal frame type in 0-RTT", +4573 )); +4574 } +4575 _ => { +4576 if frame.is_1rtt() { +4577 return Err(TransportError::PROTOCOL_VIOLATION( +4578 "illegal frame type in 0-RTT", +4579 )); +4580 } +4581 } +4582 } +4583 } +4584 ack_eliciting |= frame.is_ack_eliciting(); +4585 +4586 // Check whether this could be a probing packet +4587 match frame { +4588 Frame::Padding +4589 | Frame::PathChallenge(_) +4590 | Frame::PathResponse(_) +4591 | Frame::NewConnectionId(_) +4592 | Frame::ObservedAddr(_) => {} +4593 _ => { +4594 is_probing_packet = false; +4595 } +4596 } +4597 +4598 match frame { +4599 Frame::Crypto(frame) => { +4600 self.read_crypto(SpaceId::Data, &frame, payload_len)?; +4601 } +4602 Frame::Stream(frame) => { +4603 if self.streams.received(frame, payload_len)?.should_transmit() { +4604 self.spaces[SpaceId::Data].pending.max_data = true; +4605 } +4606 } +4607 Frame::Ack(ack) => { +4608 self.on_ack_received(now, SpaceId::Data, ack)?; 4609 } -4610 Frame::Ack(ack) => { -4611 self.on_ack_received(now, SpaceId::Data, ack)?; -4612 } -4613 Frame::PathAck(ack) => { -4614 span.record("path", tracing::field::display(&ack.path_id)); -4615 self.on_path_ack_received(now, SpaceId::Data, ack)?; -4616 } -4617 Frame::Padding | Frame::Ping => {} -4618 Frame::Close(reason) => { -4619 close = Some(reason); -4620 } -4621 Frame::PathChallenge(challenge) => { -4622 let path = &mut self -4623 .path_mut(path_id) -4624 .expect("payload is processed only after the path becomes known"); -4625 path.path_responses.push(number, challenge.0, network_path); -4626 // At this point, update_network_path_or_discard was already called, so -4627 // we don't need to be lenient about `local_ip` possibly mis-matching. -4628 if network_path == path.network_path { -4629 // PATH_CHALLENGE on active path, possible off-path packet forwarding -4630 // attack. Send a non-probing packet to recover the active path. -4631 // TODO(flub): No longer true! We now path_challege also to validate -4632 // the path if the path is new, without an RFC9000-style -4633 // migration involved. This means we add in an extra -4634 // IMMEDIATE_ACK on some challenges. It isn't really wrong to do -4635 // so, but it still is something untidy. We should instead -4636 // suppress this when we know the remote is still validating the -4637 // path. -4638 match self.peer_supports_ack_frequency() { -4639 true => self.immediate_ack(path_id), -4640 false => { -4641 self.ping_path(path_id).ok(); -4642 } -4643 } -4644 } -4645 } -4646 Frame::PathResponse(response) => { -4647 let path = self -4648 .paths -4649 .get_mut(&path_id) -4650 .expect("payload is processed only after the path becomes known"); -4651 -4652 use PathTimer::*; -4653 use paths::OnPathResponseReceived::*; -4654 match path -4655 .data -4656 .on_path_response_received(now, response.0, network_path) -4657 { -4658 OnPath { was_open } => { -4659 let qlog = self.qlog.with_time(now); -4660 -4661 self.timers -4662 .stop(Timer::PerPath(path_id, PathValidation), qlog.clone()); -4663 self.timers -4664 .stop(Timer::PerPath(path_id, PathOpen), qlog.clone()); -4665 -4666 let next_challenge = path -4667 .data -4668 .earliest_expiring_challenge() -4669 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); -4670 self.timers.set_or_stop( -4671 Timer::PerPath(path_id, PathChallengeLost), -4672 next_challenge, -4673 qlog, -4674 ); -4675 -4676 if !was_open { -4677 if is_multipath_negotiated { -4678 self.events -4679 .push_back(Event::Path(PathEvent::Opened { id: path_id })); -4680 } -4681 if let Some(observed) = path.data.last_observed_addr_report.as_ref() -4682 { -4683 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -4684 id: path_id, -4685 addr: observed.socket_addr(), -4686 })); -4687 } -4688 } -4689 if let Some((_, ref mut prev)) = path.prev { -4690 prev.challenges_sent.clear(); -4691 prev.send_new_challenge = false; -4692 } -4693 } -4694 OffPath => { -4695 debug!("Response to off-path PathChallenge!"); -4696 let next_challenge = path -4697 .data -4698 .earliest_expiring_challenge() -4699 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); -4700 self.timers.set_or_stop( -4701 Timer::PerPath(path_id, PathChallengeLost), -4702 next_challenge, -4703 self.qlog.with_time(now), -4704 ); +4610 Frame::PathAck(ack) => { +4611 span.record("path", tracing::field::display(&ack.path_id)); +4612 self.on_path_ack_received(now, SpaceId::Data, ack)?; +4613 } +4614 Frame::Padding | Frame::Ping => {} +4615 Frame::Close(reason) => { +4616 close = Some(reason); +4617 } +4618 Frame::PathChallenge(challenge) => { +4619 let path = &mut self +4620 .path_mut(path_id) +4621 .expect("payload is processed only after the path becomes known"); +4622 path.path_responses.push(number, challenge.0, network_path); +4623 // At this point, update_network_path_or_discard was already called, so +4624 // we don't need to be lenient about `local_ip` possibly mis-matching. +4625 if network_path == path.network_path { +4626 // PATH_CHALLENGE on active path, possible off-path packet forwarding +4627 // attack. Send a non-probing packet to recover the active path. +4628 // TODO(flub): No longer true! We now path_challege also to validate +4629 // the path if the path is new, without an RFC9000-style +4630 // migration involved. This means we add in an extra +4631 // IMMEDIATE_ACK on some challenges. It isn't really wrong to do +4632 // so, but it still is something untidy. We should instead +4633 // suppress this when we know the remote is still validating the +4634 // path. +4635 match self.peer_supports_ack_frequency() { +4636 true => self.immediate_ack(path_id), +4637 false => { +4638 self.ping_path(path_id).ok(); +4639 } +4640 } +4641 } +4642 } +4643 Frame::PathResponse(response) => { +4644 let path = self +4645 .paths +4646 .get_mut(&path_id) +4647 .expect("payload is processed only after the path becomes known"); +4648 +4649 use PathTimer::*; +4650 use paths::OnPathResponseReceived::*; +4651 match path +4652 .data +4653 .on_path_response_received(now, response.0, network_path) +4654 { +4655 OnPath { was_open } => { +4656 let qlog = self.qlog.with_time(now); +4657 +4658 self.timers +4659 .stop(Timer::PerPath(path_id, PathValidation), qlog.clone()); +4660 self.timers +4661 .stop(Timer::PerPath(path_id, PathOpen), qlog.clone()); +4662 +4663 let next_challenge = path +4664 .data +4665 .earliest_expiring_challenge() +4666 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); +4667 self.timers.set_or_stop( +4668 Timer::PerPath(path_id, PathChallengeLost), +4669 next_challenge, +4670 qlog, +4671 ); +4672 +4673 if !was_open { +4674 if is_multipath_negotiated { +4675 self.events +4676 .push_back(Event::Path(PathEvent::Opened { id: path_id })); +4677 } +4678 if let Some(observed) = path.data.last_observed_addr_report.as_ref() +4679 { +4680 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4681 id: path_id, +4682 addr: observed.socket_addr(), +4683 })); +4684 } +4685 } +4686 if let Some((_, ref mut prev)) = path.prev { +4687 prev.challenges_sent.clear(); +4688 prev.send_new_challenge = false; +4689 } +4690 } +4691 OffPath => { +4692 debug!("Response to off-path PathChallenge!"); +4693 let next_challenge = path +4694 .data +4695 .earliest_expiring_challenge() +4696 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); +4697 self.timers.set_or_stop( +4698 Timer::PerPath(path_id, PathChallengeLost), +4699 next_challenge, +4700 self.qlog.with_time(now), +4701 ); +4702 } +4703 Invalid { expected } => { +4704 debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE") 4705 } -4706 Invalid { expected } => { -4707 debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE") -4708 } -4709 Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"), -4710 } +4706 Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"), +4707 } +4708 } +4709 Frame::MaxData(frame::MaxData(bytes)) => { +4710 self.streams.received_max_data(bytes); 4711 } -4712 Frame::MaxData(frame::MaxData(bytes)) => { -4713 self.streams.received_max_data(bytes); +4712 Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => { +4713 self.streams.received_max_stream_data(id, offset)?; 4714 } -4715 Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => { -4716 self.streams.received_max_stream_data(id, offset)?; +4715 Frame::MaxStreams(frame::MaxStreams { dir, count }) => { +4716 self.streams.received_max_streams(dir, count)?; 4717 } -4718 Frame::MaxStreams(frame::MaxStreams { dir, count }) => { -4719 self.streams.received_max_streams(dir, count)?; -4720 } -4721 Frame::ResetStream(frame) => { -4722 if self.streams.received_reset(frame)?.should_transmit() { -4723 self.spaces[SpaceId::Data].pending.max_data = true; -4724 } +4718 Frame::ResetStream(frame) => { +4719 if self.streams.received_reset(frame)?.should_transmit() { +4720 self.spaces[SpaceId::Data].pending.max_data = true; +4721 } +4722 } +4723 Frame::DataBlocked(DataBlocked(offset)) => { +4724 debug!(offset, "peer claims to be blocked at connection level"); 4725 } -4726 Frame::DataBlocked(DataBlocked(offset)) => { -4727 debug!(offset, "peer claims to be blocked at connection level"); -4728 } -4729 Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => { -4730 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { -4731 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); -4732 return Err(TransportError::STREAM_STATE_ERROR( -4733 "STREAM_DATA_BLOCKED on send-only stream", -4734 )); -4735 } -4736 debug!( -4737 stream = %id, -4738 offset, "peer claims to be blocked at stream level" -4739 ); -4740 } -4741 Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => { -4742 if limit > MAX_STREAM_COUNT { -4743 return Err(TransportError::FRAME_ENCODING_ERROR( -4744 "unrepresentable stream limit", -4745 )); -4746 } -4747 debug!( -4748 "peer claims to be blocked opening more than {} {} streams", -4749 limit, dir -4750 ); -4751 } -4752 Frame::StopSending(frame::StopSending { id, error_code }) => { -4753 if id.initiator() != self.side.side() { -4754 if id.dir() == Dir::Uni { -4755 debug!("got STOP_SENDING on recv-only {}", id); -4756 return Err(TransportError::STREAM_STATE_ERROR( -4757 "STOP_SENDING on recv-only stream", -4758 )); -4759 } -4760 } else if self.streams.is_local_unopened(id) { -4761 return Err(TransportError::STREAM_STATE_ERROR( -4762 "STOP_SENDING on unopened stream", -4763 )); -4764 } -4765 self.streams.received_stop_sending(id, error_code); -4766 } -4767 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { -4768 if let Some(ref path_id) = path_id { -4769 span.record("path", tracing::field::display(&path_id)); -4770 } -4771 let path_id = path_id.unwrap_or_default(); -4772 match self.local_cid_state.get_mut(&path_id) { -4773 None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), -4774 Some(cid_state) => { -4775 let allow_more_cids = cid_state -4776 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; -4777 -4778 // If the path has closed, we do not issue more CIDs for this path -4779 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 -4780 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. -4781 let has_path = !self.abandoned_paths.contains(&path_id); -4782 let allow_more_cids = allow_more_cids && has_path; -4783 -4784 self.endpoint_events -4785 .push_back(EndpointEventInner::RetireConnectionId( -4786 now, -4787 path_id, -4788 sequence, -4789 allow_more_cids, -4790 )); -4791 } -4792 } -4793 } -4794 Frame::NewConnectionId(frame) => { -4795 let path_id = if let Some(path_id) = frame.path_id { -4796 if !self.is_multipath_negotiated() { -4797 return Err(TransportError::PROTOCOL_VIOLATION( -4798 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", -4799 )); -4800 } -4801 if path_id > self.local_max_path_id { -4802 return Err(TransportError::PROTOCOL_VIOLATION( -4803 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", -4804 )); -4805 } -4806 path_id -4807 } else { -4808 PathId::ZERO -4809 }; -4810 -4811 if let Some(ref path_id) = frame.path_id { -4812 span.record("path", tracing::field::display(&path_id)); -4813 } -4814 -4815 if self.abandoned_paths.contains(&path_id) { -4816 trace!("ignoring issued CID for abandoned path"); -4817 continue; -4818 } -4819 let remote_cids = self -4820 .remote_cids -4821 .entry(path_id) -4822 .or_insert_with(|| CidQueue::new(frame.id)); -4823 if remote_cids.active().is_empty() { -4824 return Err(TransportError::PROTOCOL_VIOLATION( -4825 "NEW_CONNECTION_ID when CIDs aren't in use", -4826 )); -4827 } -4828 if frame.retire_prior_to > frame.sequence { -4829 return Err(TransportError::PROTOCOL_VIOLATION( -4830 "NEW_CONNECTION_ID retiring unissued CIDs", -4831 )); -4832 } -4833 -4834 use crate::cid_queue::InsertError; -4835 match remote_cids.insert(frame) { -4836 Ok(None) if self.path(path_id).is_none() => { -4837 // if this gives us CIDs to open a new path and a nat traversal attempt -4838 // is underway we could try to probe a pending remote -4839 self.continue_nat_traversal_round(now); -4840 } -4841 Ok(None) => {} -4842 Ok(Some((retired, reset_token))) => { -4843 let pending_retired = -4844 &mut self.spaces[SpaceId::Data].pending.retire_cids; -4845 /// Ensure `pending_retired` cannot grow without bound. Limit is -4846 /// somewhat arbitrary but very permissive. -4847 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; -4848 // We don't bother counting in-flight frames because those are bounded -4849 // by congestion control. -4850 if (pending_retired.len() as u64) -4851 .saturating_add(retired.end.saturating_sub(retired.start)) -4852 > MAX_PENDING_RETIRED_CIDS -4853 { -4854 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( -4855 "queued too many retired CIDs", -4856 )); -4857 } -4858 pending_retired.extend(retired.map(|seq| (path_id, seq))); -4859 // TODO(matheus23): Reset token for a remote or a full 4-tuple? -4860 self.set_reset_token(path_id, network_path.remote, reset_token); +4726 Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => { +4727 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { +4728 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); +4729 return Err(TransportError::STREAM_STATE_ERROR( +4730 "STREAM_DATA_BLOCKED on send-only stream", +4731 )); +4732 } +4733 debug!( +4734 stream = %id, +4735 offset, "peer claims to be blocked at stream level" +4736 ); +4737 } +4738 Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => { +4739 if limit > MAX_STREAM_COUNT { +4740 return Err(TransportError::FRAME_ENCODING_ERROR( +4741 "unrepresentable stream limit", +4742 )); +4743 } +4744 debug!( +4745 "peer claims to be blocked opening more than {} {} streams", +4746 limit, dir +4747 ); +4748 } +4749 Frame::StopSending(frame::StopSending { id, error_code }) => { +4750 if id.initiator() != self.side.side() { +4751 if id.dir() == Dir::Uni { +4752 debug!("got STOP_SENDING on recv-only {}", id); +4753 return Err(TransportError::STREAM_STATE_ERROR( +4754 "STOP_SENDING on recv-only stream", +4755 )); +4756 } +4757 } else if self.streams.is_local_unopened(id) { +4758 return Err(TransportError::STREAM_STATE_ERROR( +4759 "STOP_SENDING on unopened stream", +4760 )); +4761 } +4762 self.streams.received_stop_sending(id, error_code); +4763 } +4764 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { +4765 if let Some(ref path_id) = path_id { +4766 span.record("path", tracing::field::display(&path_id)); +4767 } +4768 let path_id = path_id.unwrap_or_default(); +4769 match self.local_cid_state.get_mut(&path_id) { +4770 None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), +4771 Some(cid_state) => { +4772 let allow_more_cids = cid_state +4773 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; +4774 +4775 // If the path has closed, we do not issue more CIDs for this path +4776 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 +4777 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. +4778 let has_path = !self.abandoned_paths.contains(&path_id); +4779 let allow_more_cids = allow_more_cids && has_path; +4780 +4781 self.endpoint_events +4782 .push_back(EndpointEventInner::RetireConnectionId( +4783 now, +4784 path_id, +4785 sequence, +4786 allow_more_cids, +4787 )); +4788 } +4789 } +4790 } +4791 Frame::NewConnectionId(frame) => { +4792 let path_id = if let Some(path_id) = frame.path_id { +4793 if !self.is_multipath_negotiated() { +4794 return Err(TransportError::PROTOCOL_VIOLATION( +4795 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", +4796 )); +4797 } +4798 if path_id > self.local_max_path_id { +4799 return Err(TransportError::PROTOCOL_VIOLATION( +4800 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", +4801 )); +4802 } +4803 path_id +4804 } else { +4805 PathId::ZERO +4806 }; +4807 +4808 if let Some(ref path_id) = frame.path_id { +4809 span.record("path", tracing::field::display(&path_id)); +4810 } +4811 +4812 if self.abandoned_paths.contains(&path_id) { +4813 trace!("ignoring issued CID for abandoned path"); +4814 continue; +4815 } +4816 let remote_cids = self +4817 .remote_cids +4818 .entry(path_id) +4819 .or_insert_with(|| CidQueue::new(frame.id)); +4820 if remote_cids.active().is_empty() { +4821 return Err(TransportError::PROTOCOL_VIOLATION( +4822 "NEW_CONNECTION_ID when CIDs aren't in use", +4823 )); +4824 } +4825 if frame.retire_prior_to > frame.sequence { +4826 return Err(TransportError::PROTOCOL_VIOLATION( +4827 "NEW_CONNECTION_ID retiring unissued CIDs", +4828 )); +4829 } +4830 +4831 use crate::cid_queue::InsertError; +4832 match remote_cids.insert(frame) { +4833 Ok(None) if self.path(path_id).is_none() => { +4834 // if this gives us CIDs to open a new path and a nat traversal attempt +4835 // is underway we could try to probe a pending remote +4836 self.continue_nat_traversal_round(now); +4837 } +4838 Ok(None) => {} +4839 Ok(Some((retired, reset_token))) => { +4840 let pending_retired = +4841 &mut self.spaces[SpaceId::Data].pending.retire_cids; +4842 /// Ensure `pending_retired` cannot grow without bound. Limit is +4843 /// somewhat arbitrary but very permissive. +4844 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; +4845 // We don't bother counting in-flight frames because those are bounded +4846 // by congestion control. +4847 if (pending_retired.len() as u64) +4848 .saturating_add(retired.end.saturating_sub(retired.start)) +4849 > MAX_PENDING_RETIRED_CIDS +4850 { +4851 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( +4852 "queued too many retired CIDs", +4853 )); +4854 } +4855 pending_retired.extend(retired.map(|seq| (path_id, seq))); +4856 // TODO(matheus23): Reset token for a remote or a full 4-tuple? +4857 self.set_reset_token(path_id, network_path.remote, reset_token); +4858 } +4859 Err(InsertError::ExceedsLimit) => { +4860 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); 4861 } -4862 Err(InsertError::ExceedsLimit) => { -4863 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); -4864 } -4865 Err(InsertError::Retired) => { -4866 trace!("discarding already-retired"); -4867 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a -4868 // range of connection IDs larger than the active connection ID limit -4869 // was retired all at once via retire_prior_to. -4870 self.spaces[SpaceId::Data] -4871 .pending -4872 .retire_cids -4873 .push((path_id, frame.sequence)); -4874 continue; -4875 } -4876 }; -4877 -4878 if self.side.is_server() -4879 && path_id == PathId::ZERO -4880 && self -4881 .remote_cids -4882 .get(&PathId::ZERO) -4883 .map(|cids| cids.active_seq() == 0) -4884 .unwrap_or_default() -4885 { -4886 // We're a server still using the initial remote CID for the client, so -4887 // let's switch immediately to enable clientside stateless resets. -4888 self.update_remote_cid(PathId::ZERO); -4889 } -4890 } -4891 Frame::NewToken(NewToken { token }) => { -4892 let ConnectionSide::Client { -4893 token_store, -4894 server_name, -4895 .. -4896 } = &self.side -4897 else { -4898 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); -4899 }; -4900 if token.is_empty() { -4901 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); -4902 } -4903 trace!("got new token"); -4904 token_store.insert(server_name, token); -4905 } -4906 Frame::Datagram(datagram) => { -4907 if self -4908 .datagrams -4909 .received(datagram, &self.config.datagram_receive_buffer_size)? -4910 { -4911 self.events.push_back(Event::DatagramReceived); -4912 } -4913 } -4914 Frame::AckFrequency(ack_frequency) => { -4915 // This frame can only be sent in the Data space -4916 -4917 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { -4918 // The AckFrequency frame is stale (we have already received a more -4919 // recent one) -4920 continue; -4921 } -4922 -4923 // Update the params for all of our paths -4924 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { -4925 space.pending_acks.set_ack_frequency_params(&ack_frequency); -4926 -4927 // Our `max_ack_delay` has been updated, so we may need to adjust -4928 // its associated timeout -4929 if let Some(timeout) = space -4930 .pending_acks -4931 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) -4932 { -4933 self.timers.set( -4934 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -4935 timeout, -4936 self.qlog.with_time(now), -4937 ); -4938 } -4939 } -4940 } -4941 Frame::ImmediateAck => { -4942 // This frame can only be sent in the Data space -4943 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { -4944 pns.pending_acks.set_immediate_ack_required(); -4945 } -4946 } -4947 Frame::HandshakeDone => { -4948 if self.side.is_server() { -4949 return Err(TransportError::PROTOCOL_VIOLATION( -4950 "client sent HANDSHAKE_DONE", -4951 )); +4862 Err(InsertError::Retired) => { +4863 trace!("discarding already-retired"); +4864 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a +4865 // range of connection IDs larger than the active connection ID limit +4866 // was retired all at once via retire_prior_to. +4867 self.spaces[SpaceId::Data] +4868 .pending +4869 .retire_cids +4870 .push((path_id, frame.sequence)); +4871 continue; +4872 } +4873 }; +4874 +4875 if self.side.is_server() +4876 && path_id == PathId::ZERO +4877 && self +4878 .remote_cids +4879 .get(&PathId::ZERO) +4880 .map(|cids| cids.active_seq() == 0) +4881 .unwrap_or_default() +4882 { +4883 // We're a server still using the initial remote CID for the client, so +4884 // let's switch immediately to enable clientside stateless resets. +4885 self.update_remote_cid(PathId::ZERO); +4886 } +4887 } +4888 Frame::NewToken(NewToken { token }) => { +4889 let ConnectionSide::Client { +4890 token_store, +4891 server_name, +4892 .. +4893 } = &self.side +4894 else { +4895 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); +4896 }; +4897 if token.is_empty() { +4898 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); +4899 } +4900 trace!("got new token"); +4901 token_store.insert(server_name, token); +4902 } +4903 Frame::Datagram(datagram) => { +4904 if self +4905 .datagrams +4906 .received(datagram, &self.config.datagram_receive_buffer_size)? +4907 { +4908 self.events.push_back(Event::DatagramReceived); +4909 } +4910 } +4911 Frame::AckFrequency(ack_frequency) => { +4912 // This frame can only be sent in the Data space +4913 +4914 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { +4915 // The AckFrequency frame is stale (we have already received a more +4916 // recent one) +4917 continue; +4918 } +4919 +4920 // Update the params for all of our paths +4921 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { +4922 space.pending_acks.set_ack_frequency_params(&ack_frequency); +4923 +4924 // Our `max_ack_delay` has been updated, so we may need to adjust +4925 // its associated timeout +4926 if let Some(timeout) = space +4927 .pending_acks +4928 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) +4929 { +4930 self.timers.set( +4931 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +4932 timeout, +4933 self.qlog.with_time(now), +4934 ); +4935 } +4936 } +4937 } +4938 Frame::ImmediateAck => { +4939 // This frame can only be sent in the Data space +4940 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { +4941 pns.pending_acks.set_immediate_ack_required(); +4942 } +4943 } +4944 Frame::HandshakeDone => { +4945 if self.side.is_server() { +4946 return Err(TransportError::PROTOCOL_VIOLATION( +4947 "client sent HANDSHAKE_DONE", +4948 )); +4949 } +4950 if self.crypto_state.has_keys(EncryptionLevel::Handshake) { +4951 self.discard_space(now, SpaceKind::Handshake); 4952 } -4953 if self.crypto_state.has_keys(EncryptionLevel::Handshake) { -4954 self.discard_space(now, SpaceKind::Handshake); -4955 } -4956 self.events.push_back(Event::HandshakeConfirmed); -4957 trace!("handshake confirmed"); -4958 } -4959 Frame::ObservedAddr(observed) => { -4960 // check if params allows the peer to send report and this node to receive it -4961 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); -4962 if !self -4963 .peer_params -4964 .address_discovery_role -4965 .should_report(&self.config.address_discovery_role) -4966 { -4967 return Err(TransportError::PROTOCOL_VIOLATION( -4968 "received OBSERVED_ADDRESS frame when not negotiated", -4969 )); -4970 } -4971 // must only be sent in data space -4972 if packet.header.space() != SpaceKind::Data { -4973 return Err(TransportError::PROTOCOL_VIOLATION( -4974 "OBSERVED_ADDRESS frame outside data space", -4975 )); -4976 } -4977 -4978 let path = self.path_data_mut(path_id); -4979 if network_path == path.network_path { -4980 if let Some(updated) = path.update_observed_addr_report(observed) -4981 && path.open -4982 { -4983 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -4984 id: path_id, -4985 addr: updated, -4986 })); -4987 // otherwise the event is reported when the path is deemed open -4988 } -4989 } else { -4990 // include in migration -4991 migration_observed_addr = Some(observed) -4992 } -4993 } -4994 Frame::PathAbandon(frame::PathAbandon { -4995 path_id, -4996 error_code, -4997 }) => { -4998 span.record("path", tracing::field::display(&path_id)); -4999 // TODO(flub): don't really know which error code to use here. -5000 let locally_initiated = false; -5001 match self.close_path_inner(now, path_id, error_code.into(), locally_initiated) -5002 { -5003 Ok(()) => { -5004 trace!("peer abandoned path"); -5005 } -5006 Err(ClosePathError::LastOpenPath) => { -5007 trace!("peer abandoned last path, closing connection"); -5008 return Err(TransportError::NO_VIABLE_PATH( -5009 "last path abandoned by peer", -5010 )); +4953 self.events.push_back(Event::HandshakeConfirmed); +4954 trace!("handshake confirmed"); +4955 } +4956 Frame::ObservedAddr(observed) => { +4957 // check if params allows the peer to send report and this node to receive it +4958 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); +4959 if !self +4960 .peer_params +4961 .address_discovery_role +4962 .should_report(&self.config.address_discovery_role) +4963 { +4964 return Err(TransportError::PROTOCOL_VIOLATION( +4965 "received OBSERVED_ADDRESS frame when not negotiated", +4966 )); +4967 } +4968 // must only be sent in data space +4969 if packet.header.space() != SpaceKind::Data { +4970 return Err(TransportError::PROTOCOL_VIOLATION( +4971 "OBSERVED_ADDRESS frame outside data space", +4972 )); +4973 } +4974 +4975 let path = self.path_data_mut(path_id); +4976 if network_path == path.network_path { +4977 if let Some(updated) = path.update_observed_addr_report(observed) +4978 && path.open +4979 { +4980 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4981 id: path_id, +4982 addr: updated, +4983 })); +4984 // otherwise the event is reported when the path is deemed open +4985 } +4986 } else { +4987 // include in migration +4988 migration_observed_addr = Some(observed) +4989 } +4990 } +4991 Frame::PathAbandon(frame::PathAbandon { +4992 path_id, +4993 error_code, +4994 }) => { +4995 span.record("path", tracing::field::display(&path_id)); +4996 // TODO(flub): don't really know which error code to use here. +4997 let locally_initiated = false; +4998 match self.close_path_inner(now, path_id, error_code.into(), locally_initiated) +4999 { +5000 Ok(()) => { +5001 trace!("peer abandoned path"); +5002 } +5003 Err(ClosePathError::LastOpenPath) => { +5004 trace!("peer abandoned last path, closing connection"); +5005 return Err(TransportError::NO_VIABLE_PATH( +5006 "last path abandoned by peer", +5007 )); +5008 } +5009 Err(ClosePathError::ClosedPath) => { +5010 trace!("peer abandoned already closed path"); 5011 } -5012 Err(ClosePathError::ClosedPath) => { -5013 trace!("peer abandoned already closed path"); -5014 } -5015 Err(ClosePathError::MultipathNotNegotiated) => { -5016 return Err(TransportError::PROTOCOL_VIOLATION( -5017 "received PATH_ABANDON frame when multipath was not negotiated", -5018 )); -5019 } -5020 }; -5021 // If we receive a retransmit of PATH_ABANDON then we may already have -5022 // abandoned this path locally. In that case the DiscardPath timer -5023 // may already have fired and we no longer have any state for this path. -5024 // Only set this timer if we still have path state. -5025 if let Some(path) = self.paths.get_mut(&path_id) -5026 && !matches!(path.data.abandon_state, AbandonState::Draining) -5027 { -5028 let ack_delay = self.ack_frequency.max_ack_delay_for_pto(); -5029 let pto = path.data.rtt.pto_base() + ack_delay; -5030 self.timers.set( -5031 Timer::PerPath(path_id, PathTimer::DiscardPath), -5032 now + 3 * pto, -5033 self.qlog.with_time(now), -5034 ); -5035 // We received a PATH_ABANDON, we don't expect another one by a certain time. -5036 path.data.abandon_state = AbandonState::Draining; -5037 } -5038 } -5039 Frame::PathStatusAvailable(info) => { -5040 span.record("path", tracing::field::display(&info.path_id)); -5041 if self.is_multipath_negotiated() { -5042 self.on_path_status( -5043 info.path_id, -5044 PathStatus::Available, -5045 info.status_seq_no, -5046 ); -5047 } else { -5048 return Err(TransportError::PROTOCOL_VIOLATION( -5049 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", -5050 )); -5051 } -5052 } -5053 Frame::PathStatusBackup(info) => { -5054 span.record("path", tracing::field::display(&info.path_id)); -5055 if self.is_multipath_negotiated() { -5056 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); -5057 } else { -5058 return Err(TransportError::PROTOCOL_VIOLATION( -5059 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", -5060 )); -5061 } -5062 } -5063 Frame::MaxPathId(frame::MaxPathId(path_id)) => { -5064 span.record("path", tracing::field::display(&path_id)); -5065 if !self.is_multipath_negotiated() { -5066 return Err(TransportError::PROTOCOL_VIOLATION( -5067 "received MAX_PATH_ID frame when multipath was not negotiated", -5068 )); -5069 } -5070 // frames that do not increase the path id are ignored -5071 if path_id > self.remote_max_path_id { -5072 self.remote_max_path_id = path_id; -5073 self.issue_first_path_cids(now); -5074 while let Some(true) = self.continue_nat_traversal_round(now) {} -5075 } -5076 } -5077 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { -5078 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -5079 // be treated as a connection error of type PROTOCOL_VIOLATION. -5080 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -5081 if self.is_multipath_negotiated() { -5082 if max_path_id > self.local_max_path_id { -5083 return Err(TransportError::PROTOCOL_VIOLATION( -5084 "PATHS_BLOCKED maximum path identifier was larger than local maximum", -5085 )); -5086 } -5087 debug!("received PATHS_BLOCKED({:?})", max_path_id); -5088 // TODO(@divma): ensure max concurrent paths -5089 } else { -5090 return Err(TransportError::PROTOCOL_VIOLATION( -5091 "received PATHS_BLOCKED frame when not multipath was not negotiated", -5092 )); -5093 } -5094 } -5095 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { -5096 // Nothing to do. This is recorded in the frame stats, but otherwise we -5097 // always issue all CIDs we're allowed to issue, so either this is an -5098 // impatient peer or a bug on our side. -5099 -5100 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -5101 // be treated as a connection error of type PROTOCOL_VIOLATION. -5102 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -5103 if self.is_multipath_negotiated() { -5104 if path_id > self.local_max_path_id { -5105 return Err(TransportError::PROTOCOL_VIOLATION( -5106 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", -5107 )); -5108 } -5109 if next_seq.0 -5110 > self -5111 .local_cid_state -5112 .get(&path_id) -5113 .map(|cid_state| cid_state.active_seq().1 + 1) -5114 .unwrap_or_default() -5115 { -5116 return Err(TransportError::PROTOCOL_VIOLATION( -5117 "PATH_CIDS_BLOCKED next sequence number larger than in local state", -5118 )); -5119 } -5120 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); -5121 } else { -5122 return Err(TransportError::PROTOCOL_VIOLATION( -5123 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", -5124 )); -5125 } -5126 } -5127 Frame::AddAddress(addr) => { -5128 let client_state = match self.iroh_hp.client_side_mut() { -5129 Ok(state) => state, -5130 Err(err) => { -5131 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5132 "Nat traversal(ADD_ADDRESS): {err}" -5133 ))); -5134 } -5135 }; +5012 Err(ClosePathError::MultipathNotNegotiated) => { +5013 return Err(TransportError::PROTOCOL_VIOLATION( +5014 "received PATH_ABANDON frame when multipath was not negotiated", +5015 )); +5016 } +5017 }; +5018 +5019 // Start draining the path if it still exists and hasn't started draining yet. +5020 if let Some(path) = self.paths.get_mut(&path_id) +5021 && !mem::replace(&mut path.data.draining, true) +5022 { +5023 let ack_delay = self.ack_frequency.max_ack_delay_for_pto(); +5024 let pto = path.data.rtt.pto_base() + ack_delay; +5025 self.timers.set( +5026 Timer::PerPath(path_id, PathTimer::DiscardPath), +5027 now + 3 * pto, +5028 self.qlog.with_time(now), +5029 ); +5030 +5031 self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8)); +5032 } +5033 } +5034 Frame::PathStatusAvailable(info) => { +5035 span.record("path", tracing::field::display(&info.path_id)); +5036 if self.is_multipath_negotiated() { +5037 self.on_path_status( +5038 info.path_id, +5039 PathStatus::Available, +5040 info.status_seq_no, +5041 ); +5042 } else { +5043 return Err(TransportError::PROTOCOL_VIOLATION( +5044 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", +5045 )); +5046 } +5047 } +5048 Frame::PathStatusBackup(info) => { +5049 span.record("path", tracing::field::display(&info.path_id)); +5050 if self.is_multipath_negotiated() { +5051 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); +5052 } else { +5053 return Err(TransportError::PROTOCOL_VIOLATION( +5054 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", +5055 )); +5056 } +5057 } +5058 Frame::MaxPathId(frame::MaxPathId(path_id)) => { +5059 span.record("path", tracing::field::display(&path_id)); +5060 if !self.is_multipath_negotiated() { +5061 return Err(TransportError::PROTOCOL_VIOLATION( +5062 "received MAX_PATH_ID frame when multipath was not negotiated", +5063 )); +5064 } +5065 // frames that do not increase the path id are ignored +5066 if path_id > self.remote_max_path_id { +5067 self.remote_max_path_id = path_id; +5068 self.issue_first_path_cids(now); +5069 while let Some(true) = self.continue_nat_traversal_round(now) {} +5070 } +5071 } +5072 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { +5073 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +5074 // be treated as a connection error of type PROTOCOL_VIOLATION. +5075 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +5076 if self.is_multipath_negotiated() { +5077 if max_path_id > self.local_max_path_id { +5078 return Err(TransportError::PROTOCOL_VIOLATION( +5079 "PATHS_BLOCKED maximum path identifier was larger than local maximum", +5080 )); +5081 } +5082 debug!("received PATHS_BLOCKED({:?})", max_path_id); +5083 // TODO(@divma): ensure max concurrent paths +5084 } else { +5085 return Err(TransportError::PROTOCOL_VIOLATION( +5086 "received PATHS_BLOCKED frame when not multipath was not negotiated", +5087 )); +5088 } +5089 } +5090 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { +5091 // Nothing to do. This is recorded in the frame stats, but otherwise we +5092 // always issue all CIDs we're allowed to issue, so either this is an +5093 // impatient peer or a bug on our side. +5094 +5095 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +5096 // be treated as a connection error of type PROTOCOL_VIOLATION. +5097 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +5098 if self.is_multipath_negotiated() { +5099 if path_id > self.local_max_path_id { +5100 return Err(TransportError::PROTOCOL_VIOLATION( +5101 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", +5102 )); +5103 } +5104 if next_seq.0 +5105 > self +5106 .local_cid_state +5107 .get(&path_id) +5108 .map(|cid_state| cid_state.active_seq().1 + 1) +5109 .unwrap_or_default() +5110 { +5111 return Err(TransportError::PROTOCOL_VIOLATION( +5112 "PATH_CIDS_BLOCKED next sequence number larger than in local state", +5113 )); +5114 } +5115 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); +5116 } else { +5117 return Err(TransportError::PROTOCOL_VIOLATION( +5118 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", +5119 )); +5120 } +5121 } +5122 Frame::AddAddress(addr) => { +5123 let client_state = match self.iroh_hp.client_side_mut() { +5124 Ok(state) => state, +5125 Err(err) => { +5126 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5127 "Nat traversal(ADD_ADDRESS): {err}" +5128 ))); +5129 } +5130 }; +5131 +5132 if !client_state.check_remote_address(&addr) { +5133 // if the address is not valid we flag it, but update anyway +5134 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); +5135 } 5136 -5137 if !client_state.check_remote_address(&addr) { -5138 // if the address is not valid we flag it, but update anyway -5139 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); -5140 } -5141 -5142 match client_state.add_remote_address(addr) { -5143 Ok(maybe_added) => { -5144 if let Some(added) = maybe_added { -5145 self.events.push_back(Event::NatTraversal( -5146 iroh_hp::Event::AddressAdded(added), -5147 )); -5148 } -5149 } -5150 Err(e) => { -5151 warn!(%e, "failed to add remote address") -5152 } -5153 } -5154 } -5155 Frame::RemoveAddress(addr) => { -5156 let client_state = match self.iroh_hp.client_side_mut() { -5157 Ok(state) => state, -5158 Err(err) => { -5159 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5160 "Nat traversal(REMOVE_ADDRESS): {err}" -5161 ))); -5162 } -5163 }; -5164 if let Some(removed_addr) = client_state.remove_remote_address(addr) { -5165 self.events -5166 .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved( -5167 removed_addr, -5168 ))); -5169 } -5170 } -5171 Frame::ReachOut(reach_out) => { -5172 let ipv6 = self.is_ipv6(); -5173 let server_state = match self.iroh_hp.server_side_mut() { -5174 Ok(state) => state, -5175 Err(err) => { -5176 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5177 "Nat traversal(REACH_OUT): {err}" -5178 ))); -5179 } -5180 }; -5181 -5182 if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) { -5183 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5184 "Nat traversal(REACH_OUT): {err}" -5185 ))); -5186 } -5187 } -5188 } -5189 } -5190 -5191 let space = self.spaces[SpaceId::Data].for_path(path_id); -5192 if space -5193 .pending_acks -5194 .packet_received(now, number, ack_eliciting, &space.dedup) -5195 { -5196 if self.abandoned_paths.contains(&path_id) { -5197 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from -5198 // abandoned paths. -5199 space.pending_acks.set_immediate_ack_required(); -5200 } else { -5201 self.timers.set( -5202 Timer::PerPath(path_id, PathTimer::MaxAckDelay), -5203 now + self.ack_frequency.max_ack_delay, -5204 self.qlog.with_time(now), -5205 ); -5206 } -5207 } -5208 -5209 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets -5210 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they -5211 // are only freed, and hence only issue credit, once the application has been notified -5212 // during a read on the stream. -5213 let pending = &mut self.spaces[SpaceId::Data].pending; -5214 self.streams.queue_max_stream_id(pending); +5137 match client_state.add_remote_address(addr) { +5138 Ok(maybe_added) => { +5139 if let Some(added) = maybe_added { +5140 self.events.push_back(Event::NatTraversal( +5141 iroh_hp::Event::AddressAdded(added), +5142 )); +5143 } +5144 } +5145 Err(e) => { +5146 warn!(%e, "failed to add remote address") +5147 } +5148 } +5149 } +5150 Frame::RemoveAddress(addr) => { +5151 let client_state = match self.iroh_hp.client_side_mut() { +5152 Ok(state) => state, +5153 Err(err) => { +5154 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5155 "Nat traversal(REMOVE_ADDRESS): {err}" +5156 ))); +5157 } +5158 }; +5159 if let Some(removed_addr) = client_state.remove_remote_address(addr) { +5160 self.events +5161 .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved( +5162 removed_addr, +5163 ))); +5164 } +5165 } +5166 Frame::ReachOut(reach_out) => { +5167 let ipv6 = self.is_ipv6(); +5168 let server_state = match self.iroh_hp.server_side_mut() { +5169 Ok(state) => state, +5170 Err(err) => { +5171 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5172 "Nat traversal(REACH_OUT): {err}" +5173 ))); +5174 } +5175 }; +5176 +5177 if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) { +5178 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5179 "Nat traversal(REACH_OUT): {err}" +5180 ))); +5181 } +5182 } +5183 } +5184 } +5185 +5186 let space = self.spaces[SpaceId::Data].for_path(path_id); +5187 if space +5188 .pending_acks +5189 .packet_received(now, number, ack_eliciting, &space.dedup) +5190 { +5191 if self.abandoned_paths.contains(&path_id) { +5192 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from +5193 // abandoned paths. +5194 space.pending_acks.set_immediate_ack_required(); +5195 } else { +5196 self.timers.set( +5197 Timer::PerPath(path_id, PathTimer::MaxAckDelay), +5198 now + self.ack_frequency.max_ack_delay, +5199 self.qlog.with_time(now), +5200 ); +5201 } +5202 } +5203 +5204 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets +5205 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they +5206 // are only freed, and hence only issue credit, once the application has been notified +5207 // during a read on the stream. +5208 let pending = &mut self.spaces[SpaceId::Data].pending; +5209 self.streams.queue_max_stream_id(pending); +5210 +5211 if let Some(reason) = close { +5212 self.state.move_to_draining(Some(reason.into())); +5213 self.connection_close_pending = true; +5214 } 5215 -5216 if let Some(reason) = close { -5217 self.state.move_to_draining(Some(reason.into())); -5218 self.connection_close_pending = true; -5219 } -5220 -5221 if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet -5222 && !is_probing_packet -5223 && network_path != self.path_data(path_id).network_path -5224 { -5225 let ConnectionSide::Server { ref server_config } = self.side else { -5226 panic!("packets from unknown remote should be dropped by clients"); -5227 }; -5228 debug_assert!( -5229 server_config.migration, -5230 "migration-initiating packets should have been dropped immediately" -5231 ); -5232 self.migrate(path_id, now, network_path, migration_observed_addr); -5233 // Break linkability, if possible -5234 self.update_remote_cid(path_id); -5235 self.spin = false; -5236 } -5237 -5238 Ok(()) -5239 } -5240 -5241 fn migrate( -5242 &mut self, -5243 path_id: PathId, -5244 now: Instant, -5245 network_path: FourTuple, -5246 observed_addr: Option<ObservedAddr>, -5247 ) { -5248 trace!(%network_path, %path_id, "migration initiated"); -5249 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); -5250 // TODO(@divma): conditions for path migration in multipath are very specific, check them -5251 // again to prevent path migrations that should actually create a new path -5252 -5253 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. -5254 // Note that the congestion window will not grow until validation terminates. Helps mitigate -5255 // amplification attacks performed by spoofing source addresses. -5256 let prev_pto = self.pto(SpaceKind::Data, path_id); -5257 let path = self.paths.get_mut(&path_id).expect("known path"); -5258 let mut new_path_data = if network_path.remote.is_ipv4() -5259 && network_path.remote.ip() == path.data.network_path.remote.ip() -5260 { -5261 PathData::from_previous(network_path, &path.data, self.path_generation_counter, now) -5262 } else { -5263 let peer_max_udp_payload_size = -5264 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) -5265 .unwrap_or(u16::MAX); -5266 PathData::new( -5267 network_path, -5268 self.allow_mtud, -5269 Some(peer_max_udp_payload_size), -5270 self.path_generation_counter, -5271 now, -5272 &self.config, -5273 ) -5274 }; -5275 new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone(); -5276 if let Some(report) = observed_addr -5277 && let Some(updated) = new_path_data.update_observed_addr_report(report) -5278 { -5279 tracing::info!("adding observed addr event from migration"); -5280 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -5281 id: path_id, -5282 addr: updated, -5283 })); -5284 } -5285 new_path_data.send_new_challenge = true; -5286 -5287 let mut prev_path_data = mem::replace(&mut path.data, new_path_data); -5288 -5289 // Only store this as previous path if it was validated. For all we know there could -5290 // already be a previous path stored which might have been validated in the past, -5291 // which is more valuable than one that's not yet validated. -5292 // -5293 // With multipath it is possible that there are no remote CIDs for the path ID -5294 // yet. In this case we would never have sent on this path yet and would not be able -5295 // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair -5296 // anyway. So don't store such a path either. -5297 if !prev_path_data.validated -5298 && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active) -5299 { -5300 prev_path_data.send_new_challenge = true; -5301 // We haven't updated the remote CID yet, this captures the remote CID we were using on -5302 // the previous path. -5303 path.prev = Some((cid, prev_path_data)); -5304 } -5305 -5306 // We need to re-assign the correct remote to this path in qlog -5307 self.qlog.emit_tuple_assigned(path_id, network_path, now); -5308 -5309 self.timers.set( -5310 Timer::PerPath(path_id, PathTimer::PathValidation), -5311 now + 3 * cmp::max(self.pto(SpaceKind::Data, path_id), prev_pto), -5312 self.qlog.with_time(now), -5313 ); -5314 } -5315 -5316 /// Handle a change in the local address, i.e. an active migration -5317 /// -5318 /// In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged -5319 /// for a liveness check. This is the behaviour of a path assumed to be recoverable, even if -5320 /// this is not the case. -5321 /// -5322 /// Clients in a connection in which multipath has been negotiated should migrate paths to new -5323 /// [`PathId`]s. For paths that are known to be non-recoverable can be migrated to a new -5324 /// [`PathId`] by closing the current path, and opening a new one to the same remote. Treating -5325 /// paths as non recoverable when necessary accelerates connectivity re-establishment, or might -5326 /// allow it altogether. -5327 /// -5328 /// The optional `hint` allows callers to indicate when paths are non-recoverable and should be -5329 /// migrated to new a [`PathId`]. -5330 // NOTE: only clients are allowed to migrate, but generally dealing with RFC9000 migrations is -5331 // lacking <https://github.com/n0-computer/quinn/issues/364> -5332 pub fn handle_network_change(&mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant) { -5333 debug!("network changed"); -5334 if self.highest_space < SpaceKind::Data { -5335 for path in self.paths.values_mut() { -5336 // Clear the local address for it to be obtained from the socket again. -5337 path.data.network_path.local_ip = None; -5338 } -5339 -5340 self.update_remote_cid(PathId::ZERO); -5341 self.ping(); -5342 -5343 return; -5344 } -5345 -5346 // Paths that can't recover so a new path should be open instead. If multipath is not -5347 // negotiated, this will be empty. -5348 let mut non_recoverable_paths = Vec::default(); -5349 let mut recoverable_paths = Vec::default(); -5350 let mut open_paths = 0; -5351 -5352 let is_multipath_negotiated = self.is_multipath_negotiated(); -5353 let is_client = self.side().is_client(); -5354 let immediate_ack_allowed = self.peer_supports_ack_frequency(); -5355 -5356 for (path_id, path) in self.paths.iter_mut() { -5357 if self.abandoned_paths.contains(path_id) { -5358 continue; -5359 } -5360 open_paths += 1; -5361 -5362 // Clear the local address for it to be obtained from the socket again. This applies to -5363 // all paths, regardless of being considered recoverable or not -5364 path.data.network_path.local_ip = None; -5365 -5366 let network_path = path.data.network_path; -5367 let remote = network_path.remote; -5368 -5369 // Without multipath, the connection tries to recover the single path, whereas with -5370 // multipath, even in a single-path scenario, we attempt to migrate the path to a new -5371 // PathId. -5372 let attempt_to_recover = if is_multipath_negotiated { -5373 if is_client { -5374 hint.map(|h| h.is_path_recoverable(*path_id, network_path)) -5375 .unwrap_or(false) -5376 } else { -5377 // Servers should have stable addresses so this scenario is generally discouraged. -5378 // There is no way to prevent this, so the best hope is to attempt to recover the -5379 // path -5380 true -5381 } -5382 } else { -5383 // In the non multipath case, we try to recover the single active path -5384 true -5385 }; -5386 -5387 if attempt_to_recover { -5388 recoverable_paths.push((*path_id, remote)); -5389 } else { -5390 non_recoverable_paths.push((*path_id, remote, path.data.local_status())) -5391 } -5392 } -5393 -5394 /* NON RECOVERABLE PATHS */ -5395 // This are handled first, so that in case the treatment intended for these fails, we can -5396 // go the recoverable route instead. -5397 -5398 // Decide if we need to close first or open first in the multipath case. -5399 // - Opening first has a higher risk of getting limited by the negotiated MAX_PATH_ID. -5400 // - Closing first risks this being the only open path. -5401 // We prefer closing paths first unless we identify this is the last open path. -5402 let open_first = open_paths == non_recoverable_paths.len(); -5403 -5404 let abandon_error = TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(); -5405 -5406 for (path_id, remote, status) in non_recoverable_paths.into_iter() { -5407 let network_path = FourTuple { -5408 remote, -5409 local_ip: None, /* allow the local ip to be discovered */ -5410 }; -5411 -5412 if open_first && let Err(e) = self.open_path(network_path, status, now) { -5413 debug!(%e,"Failed to open new path for network change"); -5414 // if this fails, let the path try to recover itself -5415 recoverable_paths.push((path_id, remote)); -5416 continue; -5417 } -5418 -5419 if let Err(e) = self.close_path(now, path_id, abandon_error) { -5420 debug!(%e,"Failed to close unrecoverable path after network change"); -5421 recoverable_paths.push((path_id, remote)); -5422 continue; -5423 } -5424 -5425 if !open_first && let Err(e) = self.open_path(network_path, status, now) { -5426 // Path has already been closed if we got here. Since the path was not recoverable, -5427 // this might be desirable in any case, because other paths exist (!open_first) and -5428 // this was is considered non recoverable -5429 debug!(%e,"Failed to open new path for network change"); -5430 } -5431 } +5216 if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet +5217 && !is_probing_packet +5218 && network_path != self.path_data(path_id).network_path +5219 { +5220 let ConnectionSide::Server { ref server_config } = self.side else { +5221 panic!("packets from unknown remote should be dropped by clients"); +5222 }; +5223 debug_assert!( +5224 server_config.migration, +5225 "migration-initiating packets should have been dropped immediately" +5226 ); +5227 self.migrate(path_id, now, network_path, migration_observed_addr); +5228 // Break linkability, if possible +5229 self.update_remote_cid(path_id); +5230 self.spin = false; +5231 } +5232 +5233 Ok(()) +5234 } +5235 +5236 fn migrate( +5237 &mut self, +5238 path_id: PathId, +5239 now: Instant, +5240 network_path: FourTuple, +5241 observed_addr: Option<ObservedAddr>, +5242 ) { +5243 trace!(%network_path, %path_id, "migration initiated"); +5244 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); +5245 // TODO(@divma): conditions for path migration in multipath are very specific, check them +5246 // again to prevent path migrations that should actually create a new path +5247 +5248 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. +5249 // Note that the congestion window will not grow until validation terminates. Helps mitigate +5250 // amplification attacks performed by spoofing source addresses. +5251 let prev_pto = self.pto(SpaceKind::Data, path_id); +5252 let path = self.paths.get_mut(&path_id).expect("known path"); +5253 let mut new_path_data = if network_path.remote.is_ipv4() +5254 && network_path.remote.ip() == path.data.network_path.remote.ip() +5255 { +5256 PathData::from_previous(network_path, &path.data, self.path_generation_counter, now) +5257 } else { +5258 let peer_max_udp_payload_size = +5259 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) +5260 .unwrap_or(u16::MAX); +5261 PathData::new( +5262 network_path, +5263 self.allow_mtud, +5264 Some(peer_max_udp_payload_size), +5265 self.path_generation_counter, +5266 now, +5267 &self.config, +5268 ) +5269 }; +5270 new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone(); +5271 if let Some(report) = observed_addr +5272 && let Some(updated) = new_path_data.update_observed_addr_report(report) +5273 { +5274 tracing::info!("adding observed addr event from migration"); +5275 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +5276 id: path_id, +5277 addr: updated, +5278 })); +5279 } +5280 new_path_data.send_new_challenge = true; +5281 +5282 let mut prev_path_data = mem::replace(&mut path.data, new_path_data); +5283 +5284 // Only store this as previous path if it was validated. For all we know there could +5285 // already be a previous path stored which might have been validated in the past, +5286 // which is more valuable than one that's not yet validated. +5287 // +5288 // With multipath it is possible that there are no remote CIDs for the path ID +5289 // yet. In this case we would never have sent on this path yet and would not be able +5290 // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair +5291 // anyway. So don't store such a path either. +5292 if !prev_path_data.validated +5293 && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active) +5294 { +5295 prev_path_data.send_new_challenge = true; +5296 // We haven't updated the remote CID yet, this captures the remote CID we were using on +5297 // the previous path. +5298 path.prev = Some((cid, prev_path_data)); +5299 } +5300 +5301 // We need to re-assign the correct remote to this path in qlog +5302 self.qlog.emit_tuple_assigned(path_id, network_path, now); +5303 +5304 self.timers.set( +5305 Timer::PerPath(path_id, PathTimer::PathValidation), +5306 now + 3 * cmp::max(self.pto(SpaceKind::Data, path_id), prev_pto), +5307 self.qlog.with_time(now), +5308 ); +5309 } +5310 +5311 /// Handle a change in the local address, i.e. an active migration +5312 /// +5313 /// In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged +5314 /// for a liveness check. This is the behaviour of a path assumed to be recoverable, even if +5315 /// this is not the case. +5316 /// +5317 /// Clients in a connection in which multipath has been negotiated should migrate paths to new +5318 /// [`PathId`]s. For paths that are known to be non-recoverable can be migrated to a new +5319 /// [`PathId`] by closing the current path, and opening a new one to the same remote. Treating +5320 /// paths as non recoverable when necessary accelerates connectivity re-establishment, or might +5321 /// allow it altogether. +5322 /// +5323 /// The optional `hint` allows callers to indicate when paths are non-recoverable and should be +5324 /// migrated to new a [`PathId`]. +5325 // NOTE: only clients are allowed to migrate, but generally dealing with RFC9000 migrations is +5326 // lacking <https://github.com/n0-computer/quinn/issues/364> +5327 pub fn handle_network_change(&mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant) { +5328 debug!("network changed"); +5329 if self.highest_space < SpaceKind::Data { +5330 for path in self.paths.values_mut() { +5331 // Clear the local address for it to be obtained from the socket again. +5332 path.data.network_path.local_ip = None; +5333 } +5334 +5335 self.update_remote_cid(PathId::ZERO); +5336 self.ping(); +5337 +5338 return; +5339 } +5340 +5341 // Paths that can't recover so a new path should be open instead. If multipath is not +5342 // negotiated, this will be empty. +5343 let mut non_recoverable_paths = Vec::default(); +5344 let mut recoverable_paths = Vec::default(); +5345 let mut open_paths = 0; +5346 +5347 let is_multipath_negotiated = self.is_multipath_negotiated(); +5348 let is_client = self.side().is_client(); +5349 let immediate_ack_allowed = self.peer_supports_ack_frequency(); +5350 +5351 for (path_id, path) in self.paths.iter_mut() { +5352 if self.abandoned_paths.contains(path_id) { +5353 continue; +5354 } +5355 open_paths += 1; +5356 +5357 // Clear the local address for it to be obtained from the socket again. This applies to +5358 // all paths, regardless of being considered recoverable or not +5359 path.data.network_path.local_ip = None; +5360 +5361 let network_path = path.data.network_path; +5362 let remote = network_path.remote; +5363 +5364 // Without multipath, the connection tries to recover the single path, whereas with +5365 // multipath, even in a single-path scenario, we attempt to migrate the path to a new +5366 // PathId. +5367 let attempt_to_recover = if is_multipath_negotiated { +5368 if is_client { +5369 hint.map(|h| h.is_path_recoverable(*path_id, network_path)) +5370 .unwrap_or(false) +5371 } else { +5372 // Servers should have stable addresses so this scenario is generally discouraged. +5373 // There is no way to prevent this, so the best hope is to attempt to recover the +5374 // path +5375 true +5376 } +5377 } else { +5378 // In the non multipath case, we try to recover the single active path +5379 true +5380 }; +5381 +5382 if attempt_to_recover { +5383 recoverable_paths.push((*path_id, remote)); +5384 } else { +5385 non_recoverable_paths.push((*path_id, remote, path.data.local_status())) +5386 } +5387 } +5388 +5389 /* NON RECOVERABLE PATHS */ +5390 // This are handled first, so that in case the treatment intended for these fails, we can +5391 // go the recoverable route instead. +5392 +5393 // Decide if we need to close first or open first in the multipath case. +5394 // - Opening first has a higher risk of getting limited by the negotiated MAX_PATH_ID. +5395 // - Closing first risks this being the only open path. +5396 // We prefer closing paths first unless we identify this is the last open path. +5397 let open_first = open_paths == non_recoverable_paths.len(); +5398 +5399 let abandon_error = TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(); +5400 +5401 for (path_id, remote, status) in non_recoverable_paths.into_iter() { +5402 let network_path = FourTuple { +5403 remote, +5404 local_ip: None, /* allow the local ip to be discovered */ +5405 }; +5406 +5407 if open_first && let Err(e) = self.open_path(network_path, status, now) { +5408 debug!(%e,"Failed to open new path for network change"); +5409 // if this fails, let the path try to recover itself +5410 recoverable_paths.push((path_id, remote)); +5411 continue; +5412 } +5413 +5414 if let Err(e) = self.close_path(now, path_id, abandon_error) { +5415 debug!(%e,"Failed to close unrecoverable path after network change"); +5416 recoverable_paths.push((path_id, remote)); +5417 continue; +5418 } +5419 +5420 if !open_first && let Err(e) = self.open_path(network_path, status, now) { +5421 // Path has already been closed if we got here. Since the path was not recoverable, +5422 // this might be desirable in any case, because other paths exist (!open_first) and +5423 // this was is considered non recoverable +5424 debug!(%e,"Failed to open new path for network change"); +5425 } +5426 } +5427 +5428 /* RECOVERABLE PATHS */ +5429 +5430 for (path_id, remote) in recoverable_paths.into_iter() { +5431 let space = &mut self.spaces[SpaceId::Data]; 5432 -5433 /* RECOVERABLE PATHS */ -5434 -5435 for (path_id, remote) in recoverable_paths.into_iter() { -5436 let space = &mut self.spaces[SpaceId::Data]; -5437 -5438 // Schedule a Ping for a liveness check. -5439 if let Some(path_space) = space.number_spaces.get_mut(&path_id) { -5440 path_space.ping_pending = true; +5433 // Schedule a Ping for a liveness check. +5434 if let Some(path_space) = space.number_spaces.get_mut(&path_id) { +5435 path_space.ping_pending = true; +5436 +5437 if immediate_ack_allowed { +5438 path_space.immediate_ack_pending = true; +5439 } +5440 } 5441 -5442 if immediate_ack_allowed { -5443 path_space.immediate_ack_pending = true; -5444 } -5445 } -5446 -5447 let Some((reset_token, retired)) = -5448 self.remote_cids.get_mut(&path_id).and_then(CidQueue::next) -5449 else { -5450 continue; -5451 }; -5452 -5453 // Retire the current remote CID and any CIDs we had to skip. -5454 space -5455 .pending -5456 .retire_cids -5457 .extend(retired.map(|seq| (path_id, seq))); +5442 let Some((reset_token, retired)) = +5443 self.remote_cids.get_mut(&path_id).and_then(CidQueue::next) +5444 else { +5445 continue; +5446 }; +5447 +5448 // Retire the current remote CID and any CIDs we had to skip. +5449 space +5450 .pending +5451 .retire_cids +5452 .extend(retired.map(|seq| (path_id, seq))); +5453 +5454 self.endpoint_events +5455 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); +5456 } +5457 } 5458 -5459 self.endpoint_events -5460 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); -5461 } -5462 } -5463 -5464 /// Switch to a previously unused remote connection ID, if possible -5465 fn update_remote_cid(&mut self, path_id: PathId) { -5466 let Some((reset_token, retired)) = self -5467 .remote_cids -5468 .get_mut(&path_id) -5469 .and_then(|cids| cids.next()) -5470 else { -5471 return; -5472 }; -5473 -5474 // Retire the current remote CID and any CIDs we had to skip. -5475 self.spaces[SpaceId::Data] -5476 .pending -5477 .retire_cids -5478 .extend(retired.map(|seq| (path_id, seq))); -5479 let remote = self.path_data(path_id).network_path.remote; -5480 self.set_reset_token(path_id, remote, reset_token); -5481 } -5482 -5483 /// Sends this reset token to the endpoint -5484 /// -5485 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer -5486 /// sends a reset token it knows to route it to this connection. See RFC 9000 section -5487 /// 10.3. Stateless Reset. -5488 /// -5489 /// Reset tokens are different for each path, the endpoint identifies paths by peer -5490 /// socket address however, not by path ID. -5491 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { -5492 self.endpoint_events -5493 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); -5494 -5495 // During the handshake the server sends a reset token in the transport -5496 // parameters. When we are the client and we receive the reset token during the -5497 // handshake we want this to affect our peer transport parameters. -5498 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten -5499 // shortly after this was called. And then the params don't have this anymore. -5500 if path_id == PathId::ZERO { -5501 self.peer_params.stateless_reset_token = Some(reset_token); -5502 } -5503 } -5504 -5505 /// Issue an initial set of connection IDs to the peer upon connection -5506 fn issue_first_cids(&mut self, now: Instant) { -5507 if self -5508 .local_cid_state -5509 .get(&PathId::ZERO) -5510 .expect("PathId::ZERO exists when the connection is created") -5511 .cid_len() -5512 == 0 -5513 { -5514 return; -5515 } -5516 -5517 // Subtract 1 to account for the CID we supplied while handshaking -5518 let mut n = self.peer_params.issue_cids_limit() - 1; -5519 if let ConnectionSide::Server { server_config } = &self.side -5520 && server_config.has_preferred_address() -5521 { -5522 // We also sent a CID in the transport parameters -5523 n -= 1; -5524 } -5525 self.endpoint_events -5526 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -5527 } -5528 -5529 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued -5530 /// -5531 /// Later CIDs are issued when CIDs expire or are retired by the peer. -5532 fn issue_first_path_cids(&mut self, now: Instant) { -5533 if let Some(max_path_id) = self.max_path_id() { -5534 let mut path_id = self.max_path_id_with_cids.next(); -5535 while path_id <= max_path_id { -5536 self.endpoint_events -5537 .push_back(EndpointEventInner::NeedIdentifiers( -5538 path_id, -5539 now, -5540 self.peer_params.issue_cids_limit(), -5541 )); -5542 path_id = path_id.next(); -5543 } -5544 self.max_path_id_with_cids = max_path_id; -5545 } -5546 } -5547 -5548 /// Populates a packet with frames -5549 /// -5550 /// This tries to fit as many frames as possible into the packet. -5551 /// -5552 /// *path_exclusive_only* means to only build frames which can only be sent on this -5553 /// *path. This is used in multipath for backup paths while there is still an active -5554 /// *path. -5555 fn populate_packet<'a, 'b>( -5556 &mut self, -5557 now: Instant, -5558 space_id: SpaceId, -5559 path_id: PathId, -5560 path_exclusive_only: bool, -5561 builder: &mut PacketBuilder<'a, 'b>, -5562 ) { -5563 let pn = builder.packet_number; -5564 let is_multipath_negotiated = self.is_multipath_negotiated(); -5565 let space_has_keys = self.crypto_state.has_keys(space_id.encryption_level()); -5566 let is_0rtt = space_id == SpaceId::Data && !space_has_keys; -5567 let stats = &mut self.stats.frame_tx; -5568 let space = &mut self.spaces[space_id]; -5569 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -5570 space -5571 .for_path(path_id) -5572 .pending_acks -5573 .maybe_ack_non_eliciting(); -5574 -5575 // HANDSHAKE_DONE -5576 if !is_0rtt -5577 && !path_exclusive_only -5578 && mem::replace(&mut space.pending.handshake_done, false) -5579 { -5580 builder.write_frame(frame::HandshakeDone, stats); -5581 } -5582 -5583 // REACH_OUT -5584 if let Some((round, addresses)) = space.pending.reach_out.as_mut() -5585 && !path_exclusive_only -5586 { -5587 while let Some(local_addr) = addresses.iter().next().copied() { -5588 let local_addr = addresses.take(&local_addr).expect("found from iter"); -5589 let reach_out = frame::ReachOut::new(*round, local_addr); -5590 if builder.frame_space_remaining() > reach_out.size() { -5591 builder.write_frame(reach_out, stats); -5592 } else { -5593 addresses.insert(local_addr); -5594 break; -5595 } -5596 } -5597 if addresses.is_empty() { -5598 space.pending.reach_out = None; -5599 } -5600 } -5601 -5602 // OBSERVED_ADDR -5603 if !path_exclusive_only -5604 && space_id == SpaceId::Data -5605 && self -5606 .config -5607 .address_discovery_role -5608 .should_report(&self.peer_params.address_discovery_role) -5609 && (!path.observed_addr_sent || space.pending.observed_addr) -5610 { -5611 let frame = -5612 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); -5613 if builder.frame_space_remaining() > frame.size() { -5614 builder.write_frame(frame, stats); -5615 -5616 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); -5617 path.observed_addr_sent = true; -5618 -5619 space.pending.observed_addr = false; -5620 } +5459 /// Switch to a previously unused remote connection ID, if possible +5460 fn update_remote_cid(&mut self, path_id: PathId) { +5461 let Some((reset_token, retired)) = self +5462 .remote_cids +5463 .get_mut(&path_id) +5464 .and_then(|cids| cids.next()) +5465 else { +5466 return; +5467 }; +5468 +5469 // Retire the current remote CID and any CIDs we had to skip. +5470 self.spaces[SpaceId::Data] +5471 .pending +5472 .retire_cids +5473 .extend(retired.map(|seq| (path_id, seq))); +5474 let remote = self.path_data(path_id).network_path.remote; +5475 self.set_reset_token(path_id, remote, reset_token); +5476 } +5477 +5478 /// Sends this reset token to the endpoint +5479 /// +5480 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer +5481 /// sends a reset token it knows to route it to this connection. See RFC 9000 section +5482 /// 10.3. Stateless Reset. +5483 /// +5484 /// Reset tokens are different for each path, the endpoint identifies paths by peer +5485 /// socket address however, not by path ID. +5486 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { +5487 self.endpoint_events +5488 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); +5489 +5490 // During the handshake the server sends a reset token in the transport +5491 // parameters. When we are the client and we receive the reset token during the +5492 // handshake we want this to affect our peer transport parameters. +5493 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten +5494 // shortly after this was called. And then the params don't have this anymore. +5495 if path_id == PathId::ZERO { +5496 self.peer_params.stateless_reset_token = Some(reset_token); +5497 } +5498 } +5499 +5500 /// Issue an initial set of connection IDs to the peer upon connection +5501 fn issue_first_cids(&mut self, now: Instant) { +5502 if self +5503 .local_cid_state +5504 .get(&PathId::ZERO) +5505 .expect("PathId::ZERO exists when the connection is created") +5506 .cid_len() +5507 == 0 +5508 { +5509 return; +5510 } +5511 +5512 // Subtract 1 to account for the CID we supplied while handshaking +5513 let mut n = self.peer_params.issue_cids_limit() - 1; +5514 if let ConnectionSide::Server { server_config } = &self.side +5515 && server_config.has_preferred_address() +5516 { +5517 // We also sent a CID in the transport parameters +5518 n -= 1; +5519 } +5520 self.endpoint_events +5521 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +5522 } +5523 +5524 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued +5525 /// +5526 /// Later CIDs are issued when CIDs expire or are retired by the peer. +5527 fn issue_first_path_cids(&mut self, now: Instant) { +5528 if let Some(max_path_id) = self.max_path_id() { +5529 let mut path_id = self.max_path_id_with_cids.next(); +5530 while path_id <= max_path_id { +5531 self.endpoint_events +5532 .push_back(EndpointEventInner::NeedIdentifiers( +5533 path_id, +5534 now, +5535 self.peer_params.issue_cids_limit(), +5536 )); +5537 path_id = path_id.next(); +5538 } +5539 self.max_path_id_with_cids = max_path_id; +5540 } +5541 } +5542 +5543 /// Populates a packet with frames +5544 /// +5545 /// This tries to fit as many frames as possible into the packet. +5546 /// +5547 /// *path_exclusive_only* means to only build frames which can only be sent on this +5548 /// *path. This is used in multipath for backup paths while there is still an active +5549 /// *path. +5550 fn populate_packet<'a, 'b>( +5551 &mut self, +5552 now: Instant, +5553 space_id: SpaceId, +5554 path_id: PathId, +5555 path_exclusive_only: bool, +5556 builder: &mut PacketBuilder<'a, 'b>, +5557 ) { +5558 let pn = builder.packet_number; +5559 let is_multipath_negotiated = self.is_multipath_negotiated(); +5560 let space_has_keys = self.crypto_state.has_keys(space_id.encryption_level()); +5561 let is_0rtt = space_id == SpaceId::Data && !space_has_keys; +5562 let stats = &mut self.stats.frame_tx; +5563 let space = &mut self.spaces[space_id]; +5564 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +5565 space +5566 .for_path(path_id) +5567 .pending_acks +5568 .maybe_ack_non_eliciting(); +5569 +5570 // HANDSHAKE_DONE +5571 if !is_0rtt +5572 && !path_exclusive_only +5573 && mem::replace(&mut space.pending.handshake_done, false) +5574 { +5575 builder.write_frame(frame::HandshakeDone, stats); +5576 } +5577 +5578 // REACH_OUT +5579 if let Some((round, addresses)) = space.pending.reach_out.as_mut() +5580 && !path_exclusive_only +5581 { +5582 while let Some(local_addr) = addresses.iter().next().copied() { +5583 let local_addr = addresses.take(&local_addr).expect("found from iter"); +5584 let reach_out = frame::ReachOut::new(*round, local_addr); +5585 if builder.frame_space_remaining() > reach_out.size() { +5586 builder.write_frame(reach_out, stats); +5587 } else { +5588 addresses.insert(local_addr); +5589 break; +5590 } +5591 } +5592 if addresses.is_empty() { +5593 space.pending.reach_out = None; +5594 } +5595 } +5596 +5597 // OBSERVED_ADDR +5598 if !path_exclusive_only +5599 && space_id == SpaceId::Data +5600 && self +5601 .config +5602 .address_discovery_role +5603 .should_report(&self.peer_params.address_discovery_role) +5604 && (!path.observed_addr_sent || space.pending.observed_addr) +5605 { +5606 let frame = +5607 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); +5608 if builder.frame_space_remaining() > frame.size() { +5609 builder.write_frame(frame, stats); +5610 +5611 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); +5612 path.observed_addr_sent = true; +5613 +5614 space.pending.observed_addr = false; +5615 } +5616 } +5617 +5618 // PING +5619 if mem::replace(&mut space.for_path(path_id).ping_pending, false) { +5620 builder.write_frame(frame::Ping, stats); 5621 } 5622 -5623 // PING -5624 if mem::replace(&mut space.for_path(path_id).ping_pending, false) { -5625 builder.write_frame(frame::Ping, stats); -5626 } -5627 -5628 // IMMEDIATE_ACK -5629 if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) { -5630 debug_assert_eq!( -5631 space_id, -5632 SpaceId::Data, -5633 "immediate acks must be sent in the data space" -5634 ); -5635 builder.write_frame(frame::ImmediateAck, stats); -5636 } -5637 -5638 // ACK -5639 // TODO(flub): Should this send acks for this path anyway? -5640 -5641 if !path_exclusive_only { -5642 for path_id in space -5643 .number_spaces -5644 .iter_mut() -5645 .filter(|(_, pns)| pns.pending_acks.can_send()) -5646 .map(|(&path_id, _)| path_id) -5647 .collect::<Vec<_>>() -5648 { -5649 Self::populate_acks( -5650 now, -5651 self.receiving_ecn, -5652 path_id, -5653 space_id, -5654 space, -5655 is_multipath_negotiated, -5656 builder, -5657 stats, -5658 space_has_keys, -5659 ); -5660 } -5661 } -5662 -5663 // ACK_FREQUENCY -5664 if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) { -5665 let sequence_number = self.ack_frequency.next_sequence_number(); -5666 -5667 // Safe to unwrap because this is always provided when ACK frequency is enabled -5668 let config = self.config.ack_frequency_config.as_ref().unwrap(); -5669 -5670 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error -5671 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( -5672 path.rtt.get(), -5673 config, -5674 &self.peer_params, -5675 ); -5676 -5677 let frame = frame::AckFrequency { -5678 sequence: sequence_number, -5679 ack_eliciting_threshold: config.ack_eliciting_threshold, -5680 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), -5681 reordering_threshold: config.reordering_threshold, -5682 }; -5683 builder.write_frame(frame, stats); -5684 -5685 self.ack_frequency -5686 .ack_frequency_sent(path_id, pn, max_ack_delay); -5687 } -5688 -5689 // PATH_CHALLENGE -5690 if builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND -5691 && space_id == SpaceId::Data -5692 && path.send_new_challenge -5693 && !self.state.is_closed() -5694 // we don't want to send new challenges if we are already closing -5695 { -5696 path.send_new_challenge = false; -5697 -5698 // Generate a new challenge every time we send a new PATH_CHALLENGE -5699 let token = self.rng.random(); -5700 let info = paths::SentChallengeInfo { -5701 sent_instant: now, -5702 network_path: path.network_path, -5703 }; -5704 path.challenges_sent.insert(token, info); -5705 let challenge = frame::PathChallenge(token); -5706 trace!(frame = %challenge); -5707 builder.write_frame(challenge, stats); -5708 builder.require_padding(); -5709 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); -5710 self.timers.set( -5711 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -5712 now + pto, -5713 self.qlog.with_time(now), -5714 ); +5623 // IMMEDIATE_ACK +5624 if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) { +5625 debug_assert_eq!( +5626 space_id, +5627 SpaceId::Data, +5628 "immediate acks must be sent in the data space" +5629 ); +5630 builder.write_frame(frame::ImmediateAck, stats); +5631 } +5632 +5633 // ACK +5634 // TODO(flub): Should this send acks for this path anyway? +5635 +5636 if !path_exclusive_only { +5637 for path_id in space +5638 .number_spaces +5639 .iter_mut() +5640 .filter(|(_, pns)| pns.pending_acks.can_send()) +5641 .map(|(&path_id, _)| path_id) +5642 .collect::<Vec<_>>() +5643 { +5644 Self::populate_acks( +5645 now, +5646 self.receiving_ecn, +5647 path_id, +5648 space_id, +5649 space, +5650 is_multipath_negotiated, +5651 builder, +5652 stats, +5653 space_has_keys, +5654 ); +5655 } +5656 } +5657 +5658 // ACK_FREQUENCY +5659 if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) { +5660 let sequence_number = self.ack_frequency.next_sequence_number(); +5661 +5662 // Safe to unwrap because this is always provided when ACK frequency is enabled +5663 let config = self.config.ack_frequency_config.as_ref().unwrap(); +5664 +5665 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error +5666 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( +5667 path.rtt.get(), +5668 config, +5669 &self.peer_params, +5670 ); +5671 +5672 let frame = frame::AckFrequency { +5673 sequence: sequence_number, +5674 ack_eliciting_threshold: config.ack_eliciting_threshold, +5675 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), +5676 reordering_threshold: config.reordering_threshold, +5677 }; +5678 builder.write_frame(frame, stats); +5679 +5680 self.ack_frequency +5681 .ack_frequency_sent(path_id, pn, max_ack_delay); +5682 } +5683 +5684 // PATH_CHALLENGE +5685 if builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND +5686 && space_id == SpaceId::Data +5687 && path.send_new_challenge +5688 && !self.state.is_closed() +5689 // we don't want to send new challenges if we are already closing +5690 { +5691 path.send_new_challenge = false; +5692 +5693 // Generate a new challenge every time we send a new PATH_CHALLENGE +5694 let token = self.rng.random(); +5695 let info = paths::SentChallengeInfo { +5696 sent_instant: now, +5697 network_path: path.network_path, +5698 }; +5699 path.challenges_sent.insert(token, info); +5700 let challenge = frame::PathChallenge(token); +5701 trace!(frame = %challenge); +5702 builder.write_frame(challenge, stats); +5703 builder.require_padding(); +5704 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); +5705 self.timers.set( +5706 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +5707 now + pto, +5708 self.qlog.with_time(now), +5709 ); +5710 +5711 if is_multipath_negotiated && !path.validated && path.send_new_challenge { +5712 // queue informing the path status along with the challenge +5713 space.pending.path_status.insert(path_id); +5714 } 5715 -5716 if is_multipath_negotiated && !path.validated && path.send_new_challenge { -5717 // queue informing the path status along with the challenge -5718 space.pending.path_status.insert(path_id); -5719 } -5720 -5721 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless -5722 // of whether one has already been sent on this path. -5723 if space_id == SpaceId::Data -5724 && self -5725 .config -5726 .address_discovery_role -5727 .should_report(&self.peer_params.address_discovery_role) -5728 { -5729 let frame = frame::ObservedAddr::new( -5730 path.network_path.remote, -5731 self.next_observed_addr_seq_no, -5732 ); -5733 if builder.frame_space_remaining() > frame.size() { -5734 builder.write_frame(frame, stats); -5735 -5736 self.next_observed_addr_seq_no = -5737 self.next_observed_addr_seq_no.saturating_add(1u8); -5738 path.observed_addr_sent = true; +5716 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless +5717 // of whether one has already been sent on this path. +5718 if space_id == SpaceId::Data +5719 && self +5720 .config +5721 .address_discovery_role +5722 .should_report(&self.peer_params.address_discovery_role) +5723 { +5724 let frame = frame::ObservedAddr::new( +5725 path.network_path.remote, +5726 self.next_observed_addr_seq_no, +5727 ); +5728 if builder.frame_space_remaining() > frame.size() { +5729 builder.write_frame(frame, stats); +5730 +5731 self.next_observed_addr_seq_no = +5732 self.next_observed_addr_seq_no.saturating_add(1u8); +5733 path.observed_addr_sent = true; +5734 +5735 space.pending.observed_addr = false; +5736 } +5737 } +5738 } 5739 -5740 space.pending.observed_addr = false; -5741 } -5742 } -5743 } -5744 -5745 // PATH_RESPONSE -5746 if builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND -5747 && space_id == SpaceId::Data -5748 && let Some(token) = path.path_responses.pop_on_path(path.network_path) -5749 { -5750 let response = frame::PathResponse(token); -5751 trace!(frame = %response); -5752 builder.write_frame(response, stats); -5753 builder.require_padding(); -5754 -5755 // NOTE: this is technically not required but might be useful to ride the -5756 // request/response nature of path challenges to refresh an observation -5757 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. -5758 if space_id == SpaceId::Data -5759 && self -5760 .config -5761 .address_discovery_role -5762 .should_report(&self.peer_params.address_discovery_role) -5763 { -5764 let frame = frame::ObservedAddr::new( -5765 path.network_path.remote, -5766 self.next_observed_addr_seq_no, -5767 ); -5768 if builder.frame_space_remaining() > frame.size() { -5769 builder.write_frame(frame, stats); -5770 -5771 self.next_observed_addr_seq_no = -5772 self.next_observed_addr_seq_no.saturating_add(1u8); -5773 path.observed_addr_sent = true; +5740 // PATH_RESPONSE +5741 if builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND +5742 && space_id == SpaceId::Data +5743 && let Some(token) = path.path_responses.pop_on_path(path.network_path) +5744 { +5745 let response = frame::PathResponse(token); +5746 trace!(frame = %response); +5747 builder.write_frame(response, stats); +5748 builder.require_padding(); +5749 +5750 // NOTE: this is technically not required but might be useful to ride the +5751 // request/response nature of path challenges to refresh an observation +5752 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. +5753 if space_id == SpaceId::Data +5754 && self +5755 .config +5756 .address_discovery_role +5757 .should_report(&self.peer_params.address_discovery_role) +5758 { +5759 let frame = frame::ObservedAddr::new( +5760 path.network_path.remote, +5761 self.next_observed_addr_seq_no, +5762 ); +5763 if builder.frame_space_remaining() > frame.size() { +5764 builder.write_frame(frame, stats); +5765 +5766 self.next_observed_addr_seq_no = +5767 self.next_observed_addr_seq_no.saturating_add(1u8); +5768 path.observed_addr_sent = true; +5769 +5770 space.pending.observed_addr = false; +5771 } +5772 } +5773 } 5774 -5775 space.pending.observed_addr = false; -5776 } -5777 } -5778 } -5779 -5780 // CRYPTO -5781 while !path_exclusive_only -5782 && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND -5783 && !is_0rtt -5784 { -5785 let mut frame = match space.pending.crypto.pop_front() { -5786 Some(x) => x, -5787 None => break, -5788 }; -5789 -5790 // Calculate the maximum amount of crypto data we can store in the buffer. -5791 // Since the offset is known, we can reserve the exact size required to encode it. -5792 // For length we reserve 2bytes which allows to encode up to 2^14, -5793 // which is more than what fits into normally sized QUIC frames. -5794 let max_crypto_data_size = builder.frame_space_remaining() -5795 - 1 // Frame Type -5796 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) -5797 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes -5798 -5799 let len = frame -5800 .data -5801 .len() -5802 .min(2usize.pow(14) - 1) -5803 .min(max_crypto_data_size); +5775 // CRYPTO +5776 while !path_exclusive_only +5777 && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND +5778 && !is_0rtt +5779 { +5780 let mut frame = match space.pending.crypto.pop_front() { +5781 Some(x) => x, +5782 None => break, +5783 }; +5784 +5785 // Calculate the maximum amount of crypto data we can store in the buffer. +5786 // Since the offset is known, we can reserve the exact size required to encode it. +5787 // For length we reserve 2bytes which allows to encode up to 2^14, +5788 // which is more than what fits into normally sized QUIC frames. +5789 let max_crypto_data_size = builder.frame_space_remaining() +5790 - 1 // Frame Type +5791 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) +5792 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes +5793 +5794 let len = frame +5795 .data +5796 .len() +5797 .min(2usize.pow(14) - 1) +5798 .min(max_crypto_data_size); +5799 +5800 let data = frame.data.split_to(len); +5801 let offset = frame.offset; +5802 let truncated = frame::Crypto { offset, data }; +5803 builder.write_frame(truncated, stats); 5804 -5805 let data = frame.data.split_to(len); -5806 let offset = frame.offset; -5807 let truncated = frame::Crypto { offset, data }; -5808 builder.write_frame(truncated, stats); -5809 -5810 if !frame.data.is_empty() { -5811 frame.offset += len as u64; -5812 space.pending.crypto.push_front(frame); -5813 } -5814 } -5815 -5816 // TODO(flub): maybe this is much higher priority? -5817 // PATH_ABANDON -5818 while !path_exclusive_only -5819 && space_id == SpaceId::Data -5820 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() -5821 { -5822 let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first() -5823 else { -5824 break; -5825 }; -5826 let frame = frame::PathAbandon { -5827 path_id: abandoned_path_id, -5828 error_code, -5829 }; -5830 builder.write_frame(frame, stats); -5831 -5832 let ack_delay = self.ack_frequency.max_ack_delay_for_pto(); -5833 // We can't access path here anymore due to borrowing issues. -5834 let send_pto = self.paths.get(&path_id).unwrap().data.rtt.pto_base() + ack_delay; -5835 if let Some(abandoned_path) = self.paths.get_mut(&abandoned_path_id) { -5836 // We only want to set the deadline on the *first* PATH_ABANDON we send. -5837 // Retransmits shouldn't run this code again -5838 if matches!( -5839 abandoned_path.data.abandon_state, -5840 AbandonState::NotAbandoned -5841 ) { -5842 // The peer MUST respond with a corresponding PATH_ABANDON frame. -5843 // The other peer has 3 * PTO to do that. -5844 // This uses the PTO of the path we send on! -5845 // If the PATH_ABANDON comes in within the deadline we're giving here, then this -5846 // state will be set to `AbandonState::ReceivedPathAbandon`, essentially clearing -5847 // the deadline. If we receive a frame after the deadline, we error out with a -5848 // protocol violation. -5849 // Receiving other frames before the deadline is fine, as those might be packets -5850 // that were still in-flight. -5851 abandoned_path.data.abandon_state = AbandonState::Draining; -5852 -5853 // At some point, we need to forget about the path. -5854 // If we do so too early, then we'll have discarded the CIDs of that path and won't -5855 // handle incoming packets on that path correctly. -5856 // To give this path enough time, we assume that the peer will have received our -5857 // PATH_ABANDON within 3 * PTO of the path we sent the abandon on, -5858 // and then we give the path 3 * PTO time to make it very unlikely that there will -5859 // still be packets incoming on the path at that point. -5860 // This timer will actually get reset to a value that's likely to be even earlier -5861 // once we actually receive the PATH_ABANDON frame itself. -5862 self.timers.set( -5863 Timer::PerPath(abandoned_path_id, PathTimer::DiscardPath), -5864 now + 3 * send_pto, -5865 self.qlog.with_time(now), -5866 ); -5867 } -5868 } else { -5869 warn!("sent PATH_ABANDON after path was already discarded"); -5870 } -5871 } -5872 -5873 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP -5874 while !path_exclusive_only -5875 && space_id == SpaceId::Data -5876 && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining() -5877 { -5878 let Some(path_id) = space.pending.path_status.pop_first() else { -5879 break; -5880 }; -5881 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { -5882 trace!(%path_id, "discarding queued path status for unknown path"); -5883 continue; -5884 }; -5885 -5886 let seq = path.status.seq(); -5887 match path.local_status() { -5888 PathStatus::Available => { -5889 let frame = frame::PathStatusAvailable { -5890 path_id, -5891 status_seq_no: seq, -5892 }; -5893 builder.write_frame(frame, stats); -5894 } -5895 PathStatus::Backup => { -5896 let frame = frame::PathStatusBackup { -5897 path_id, -5898 status_seq_no: seq, -5899 }; -5900 builder.write_frame(frame, stats); -5901 } -5902 } -5903 } -5904 -5905 // MAX_PATH_ID -5906 if space_id == SpaceId::Data -5907 && !path_exclusive_only -5908 && space.pending.max_path_id -5909 && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining() -5910 { -5911 let frame = frame::MaxPathId(self.local_max_path_id); -5912 builder.write_frame(frame, stats); -5913 space.pending.max_path_id = false; -5914 } -5915 -5916 // PATHS_BLOCKED -5917 if space_id == SpaceId::Data -5918 && !path_exclusive_only -5919 && space.pending.paths_blocked -5920 && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining() -5921 { -5922 let frame = frame::PathsBlocked(self.remote_max_path_id); -5923 builder.write_frame(frame, stats); -5924 space.pending.paths_blocked = false; -5925 } -5926 -5927 // PATH_CIDS_BLOCKED -5928 while space_id == SpaceId::Data -5929 && !path_exclusive_only -5930 && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining() -5931 { -5932 let Some(path_id) = space.pending.path_cids_blocked.pop_first() else { -5933 break; -5934 }; -5935 let next_seq = match self.remote_cids.get(&path_id) { -5936 Some(cid_queue) => VarInt(cid_queue.active_seq() + 1), -5937 None => VarInt(0), -5938 }; -5939 let frame = frame::PathCidsBlocked { path_id, next_seq }; -5940 builder.write_frame(frame, stats); -5941 } -5942 -5943 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS -5944 if space_id == SpaceId::Data && !path_exclusive_only { -5945 self.streams -5946 .write_control_frames(builder, &mut space.pending, stats); -5947 } -5948 -5949 // NEW_CONNECTION_ID -5950 let cid_len = self -5951 .local_cid_state -5952 .values() -5953 .map(|cid_state| cid_state.cid_len()) -5954 .max() -5955 .expect("some local CID state must exist"); -5956 let new_cid_size_bound = -5957 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); -5958 while !path_exclusive_only && builder.frame_space_remaining() > new_cid_size_bound { -5959 let issued = match space.pending.new_cids.pop() { -5960 Some(x) => x, -5961 None => break, -5962 }; -5963 let retire_prior_to = self -5964 .local_cid_state -5965 .get(&issued.path_id) -5966 .map(|cid_state| cid_state.retire_prior_to()) -5967 .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id)); -5968 -5969 let cid_path_id = match is_multipath_negotiated { -5970 true => Some(issued.path_id), -5971 false => { -5972 debug_assert_eq!(issued.path_id, PathId::ZERO); -5973 None -5974 } -5975 }; -5976 let frame = frame::NewConnectionId { -5977 path_id: cid_path_id, -5978 sequence: issued.sequence, -5979 retire_prior_to, -5980 id: issued.id, -5981 reset_token: issued.reset_token, -5982 }; -5983 builder.write_frame(frame, stats); -5984 } -5985 -5986 // RETIRE_CONNECTION_ID -5987 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); -5988 while !path_exclusive_only && builder.frame_space_remaining() > retire_cid_bound { -5989 let (path_id, sequence) = match space.pending.retire_cids.pop() { -5990 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq), -5991 Some((path_id, seq)) => (Some(path_id), seq), -5992 None => break, -5993 }; -5994 let frame = frame::RetireConnectionId { path_id, sequence }; -5995 builder.write_frame(frame, stats); -5996 } -5997 -5998 // DATAGRAM -5999 let mut sent_datagrams = false; -6000 while !path_exclusive_only -6001 && builder.frame_space_remaining() > Datagram::SIZE_BOUND -6002 && space_id == SpaceId::Data -6003 { -6004 match self.datagrams.write(builder, stats) { -6005 true => { -6006 sent_datagrams = true; -6007 } -6008 false => break, -6009 } -6010 } -6011 if self.datagrams.send_blocked && sent_datagrams { -6012 self.events.push_back(Event::DatagramsUnblocked); -6013 self.datagrams.send_blocked = false; +5805 if !frame.data.is_empty() { +5806 frame.offset += len as u64; +5807 space.pending.crypto.push_front(frame); +5808 } +5809 } +5810 +5811 // TODO(flub): maybe this is much higher priority? +5812 // PATH_ABANDON +5813 while !path_exclusive_only +5814 && space_id == SpaceId::Data +5815 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() +5816 { +5817 let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first() +5818 else { +5819 break; +5820 }; +5821 let frame = frame::PathAbandon { +5822 path_id: abandoned_path_id, +5823 error_code, +5824 }; +5825 builder.write_frame(frame, stats); +5826 } +5827 +5828 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP +5829 while !path_exclusive_only +5830 && space_id == SpaceId::Data +5831 && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining() +5832 { +5833 let Some(path_id) = space.pending.path_status.pop_first() else { +5834 break; +5835 }; +5836 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { +5837 trace!(%path_id, "discarding queued path status for unknown path"); +5838 continue; +5839 }; +5840 +5841 let seq = path.status.seq(); +5842 match path.local_status() { +5843 PathStatus::Available => { +5844 let frame = frame::PathStatusAvailable { +5845 path_id, +5846 status_seq_no: seq, +5847 }; +5848 builder.write_frame(frame, stats); +5849 } +5850 PathStatus::Backup => { +5851 let frame = frame::PathStatusBackup { +5852 path_id, +5853 status_seq_no: seq, +5854 }; +5855 builder.write_frame(frame, stats); +5856 } +5857 } +5858 } +5859 +5860 // MAX_PATH_ID +5861 if space_id == SpaceId::Data +5862 && !path_exclusive_only +5863 && space.pending.max_path_id +5864 && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining() +5865 { +5866 let frame = frame::MaxPathId(self.local_max_path_id); +5867 builder.write_frame(frame, stats); +5868 space.pending.max_path_id = false; +5869 } +5870 +5871 // PATHS_BLOCKED +5872 if space_id == SpaceId::Data +5873 && !path_exclusive_only +5874 && space.pending.paths_blocked +5875 && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +5876 { +5877 let frame = frame::PathsBlocked(self.remote_max_path_id); +5878 builder.write_frame(frame, stats); +5879 space.pending.paths_blocked = false; +5880 } +5881 +5882 // PATH_CIDS_BLOCKED +5883 while space_id == SpaceId::Data +5884 && !path_exclusive_only +5885 && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +5886 { +5887 let Some(path_id) = space.pending.path_cids_blocked.pop_first() else { +5888 break; +5889 }; +5890 let next_seq = match self.remote_cids.get(&path_id) { +5891 Some(cid_queue) => VarInt(cid_queue.active_seq() + 1), +5892 None => VarInt(0), +5893 }; +5894 let frame = frame::PathCidsBlocked { path_id, next_seq }; +5895 builder.write_frame(frame, stats); +5896 } +5897 +5898 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS +5899 if space_id == SpaceId::Data && !path_exclusive_only { +5900 self.streams +5901 .write_control_frames(builder, &mut space.pending, stats); +5902 } +5903 +5904 // NEW_CONNECTION_ID +5905 let cid_len = self +5906 .local_cid_state +5907 .values() +5908 .map(|cid_state| cid_state.cid_len()) +5909 .max() +5910 .expect("some local CID state must exist"); +5911 let new_cid_size_bound = +5912 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); +5913 while !path_exclusive_only && builder.frame_space_remaining() > new_cid_size_bound { +5914 let issued = match space.pending.new_cids.pop() { +5915 Some(x) => x, +5916 None => break, +5917 }; +5918 let retire_prior_to = self +5919 .local_cid_state +5920 .get(&issued.path_id) +5921 .map(|cid_state| cid_state.retire_prior_to()) +5922 .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id)); +5923 +5924 let cid_path_id = match is_multipath_negotiated { +5925 true => Some(issued.path_id), +5926 false => { +5927 debug_assert_eq!(issued.path_id, PathId::ZERO); +5928 None +5929 } +5930 }; +5931 let frame = frame::NewConnectionId { +5932 path_id: cid_path_id, +5933 sequence: issued.sequence, +5934 retire_prior_to, +5935 id: issued.id, +5936 reset_token: issued.reset_token, +5937 }; +5938 builder.write_frame(frame, stats); +5939 } +5940 +5941 // RETIRE_CONNECTION_ID +5942 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); +5943 while !path_exclusive_only && builder.frame_space_remaining() > retire_cid_bound { +5944 let (path_id, sequence) = match space.pending.retire_cids.pop() { +5945 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq), +5946 Some((path_id, seq)) => (Some(path_id), seq), +5947 None => break, +5948 }; +5949 let frame = frame::RetireConnectionId { path_id, sequence }; +5950 builder.write_frame(frame, stats); +5951 } +5952 +5953 // DATAGRAM +5954 let mut sent_datagrams = false; +5955 while !path_exclusive_only +5956 && builder.frame_space_remaining() > Datagram::SIZE_BOUND +5957 && space_id == SpaceId::Data +5958 { +5959 match self.datagrams.write(builder, stats) { +5960 true => { +5961 sent_datagrams = true; +5962 } +5963 false => break, +5964 } +5965 } +5966 if self.datagrams.send_blocked && sent_datagrams { +5967 self.events.push_back(Event::DatagramsUnblocked); +5968 self.datagrams.send_blocked = false; +5969 } +5970 +5971 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +5972 +5973 // NEW_TOKEN +5974 if !path_exclusive_only { +5975 while let Some(network_path) = space.pending.new_tokens.pop() { +5976 debug_assert_eq!(space_id, SpaceId::Data); +5977 let ConnectionSide::Server { server_config } = &self.side else { +5978 panic!("NEW_TOKEN frames should not be enqueued by clients"); +5979 }; +5980 +5981 if !network_path.is_probably_same_path(&path.network_path) { +5982 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only +5983 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN +5984 // frames upon an path change. Instead, when the new path becomes validated, +5985 // NEW_TOKEN frames may be enqueued for the new path instead. +5986 continue; +5987 } +5988 +5989 let token = Token::new( +5990 TokenPayload::Validation { +5991 ip: network_path.remote.ip(), +5992 issued: server_config.time_source.now(), +5993 }, +5994 &mut self.rng, +5995 ); +5996 let new_token = NewToken { +5997 token: token.encode(&*server_config.token_key).into(), +5998 }; +5999 +6000 if builder.frame_space_remaining() < new_token.size() { +6001 space.pending.new_tokens.push(network_path); +6002 break; +6003 } +6004 +6005 builder.write_frame(new_token, stats); +6006 builder.retransmits_mut().new_tokens.push(network_path); +6007 } +6008 } +6009 +6010 // STREAM +6011 if !path_exclusive_only && space_id == SpaceId::Data { +6012 self.streams +6013 .write_stream_frames(builder, self.config.send_fairness, stats); 6014 } 6015 -6016 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -6017 -6018 // NEW_TOKEN -6019 if !path_exclusive_only { -6020 while let Some(network_path) = space.pending.new_tokens.pop() { -6021 debug_assert_eq!(space_id, SpaceId::Data); -6022 let ConnectionSide::Server { server_config } = &self.side else { -6023 panic!("NEW_TOKEN frames should not be enqueued by clients"); -6024 }; -6025 -6026 if !network_path.is_probably_same_path(&path.network_path) { -6027 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only -6028 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN -6029 // frames upon an path change. Instead, when the new path becomes validated, -6030 // NEW_TOKEN frames may be enqueued for the new path instead. -6031 continue; -6032 } -6033 -6034 let token = Token::new( -6035 TokenPayload::Validation { -6036 ip: network_path.remote.ip(), -6037 issued: server_config.time_source.now(), -6038 }, -6039 &mut self.rng, -6040 ); -6041 let new_token = NewToken { -6042 token: token.encode(&*server_config.token_key).into(), -6043 }; -6044 -6045 if builder.frame_space_remaining() < new_token.size() { -6046 space.pending.new_tokens.push(network_path); -6047 break; -6048 } -6049 -6050 builder.write_frame(new_token, stats); -6051 builder.retransmits_mut().new_tokens.push(network_path); -6052 } -6053 } -6054 -6055 // STREAM -6056 if !path_exclusive_only && space_id == SpaceId::Data { -6057 self.streams -6058 .write_stream_frames(builder, self.config.send_fairness, stats); -6059 } -6060 -6061 // ADD_ADDRESS -6062 while space_id == SpaceId::Data -6063 && !path_exclusive_only -6064 && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining() -6065 { -6066 if let Some(added_address) = space.pending.add_address.pop_last() { -6067 builder.write_frame(added_address, stats); -6068 } else { -6069 break; -6070 } -6071 } -6072 -6073 // REMOVE_ADDRESS -6074 while space_id == SpaceId::Data -6075 && !path_exclusive_only -6076 && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining() -6077 { -6078 if let Some(removed_address) = space.pending.remove_address.pop_last() { -6079 builder.write_frame(removed_address, stats); -6080 } else { -6081 break; -6082 } -6083 } -6084 } -6085 -6086 /// Write pending ACKs into a buffer -6087 fn populate_acks<'a, 'b>( -6088 now: Instant, -6089 receiving_ecn: bool, -6090 path_id: PathId, -6091 space_id: SpaceId, -6092 space: &mut PacketSpace, -6093 is_multipath_negotiated: bool, -6094 builder: &mut PacketBuilder<'a, 'b>, -6095 stats: &mut FrameStats, -6096 space_has_keys: bool, -6097 ) { -6098 // 0-RTT packets must never carry acks (which would have to be of handshake packets) -6099 debug_assert!(space_has_keys, "tried to send ACK in 0-RTT"); -6100 -6101 debug_assert!( -6102 is_multipath_negotiated || path_id == PathId::ZERO, -6103 "Only PathId::ZERO allowed without multipath (have {path_id:?})" -6104 ); -6105 if is_multipath_negotiated { -6106 debug_assert!( -6107 space_id == SpaceId::Data || path_id == PathId::ZERO, -6108 "path acks must be sent in 1RTT space (have {space_id:?})" -6109 ); -6110 } -6111 -6112 let pns = space.for_path(path_id); -6113 let ranges = pns.pending_acks.ranges(); -6114 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); -6115 let ecn = if receiving_ecn { -6116 Some(&pns.ecn_counters) -6117 } else { -6118 None -6119 }; -6120 -6121 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; -6122 // TODO: This should come from `TransportConfig` if that gets configurable. -6123 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; -6124 let delay = delay_micros >> ack_delay_exp.into_inner(); -6125 -6126 if is_multipath_negotiated && space_id == SpaceId::Data { -6127 if !ranges.is_empty() { -6128 let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn); -6129 builder.write_frame(frame, stats); -6130 } -6131 } else { -6132 builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats); -6133 } -6134 } +6016 // ADD_ADDRESS +6017 while space_id == SpaceId::Data +6018 && !path_exclusive_only +6019 && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining() +6020 { +6021 if let Some(added_address) = space.pending.add_address.pop_last() { +6022 builder.write_frame(added_address, stats); +6023 } else { +6024 break; +6025 } +6026 } +6027 +6028 // REMOVE_ADDRESS +6029 while space_id == SpaceId::Data +6030 && !path_exclusive_only +6031 && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining() +6032 { +6033 if let Some(removed_address) = space.pending.remove_address.pop_last() { +6034 builder.write_frame(removed_address, stats); +6035 } else { +6036 break; +6037 } +6038 } +6039 } +6040 +6041 /// Write pending ACKs into a buffer +6042 fn populate_acks<'a, 'b>( +6043 now: Instant, +6044 receiving_ecn: bool, +6045 path_id: PathId, +6046 space_id: SpaceId, +6047 space: &mut PacketSpace, +6048 is_multipath_negotiated: bool, +6049 builder: &mut PacketBuilder<'a, 'b>, +6050 stats: &mut FrameStats, +6051 space_has_keys: bool, +6052 ) { +6053 // 0-RTT packets must never carry acks (which would have to be of handshake packets) +6054 debug_assert!(space_has_keys, "tried to send ACK in 0-RTT"); +6055 +6056 debug_assert!( +6057 is_multipath_negotiated || path_id == PathId::ZERO, +6058 "Only PathId::ZERO allowed without multipath (have {path_id:?})" +6059 ); +6060 if is_multipath_negotiated { +6061 debug_assert!( +6062 space_id == SpaceId::Data || path_id == PathId::ZERO, +6063 "path acks must be sent in 1RTT space (have {space_id:?})" +6064 ); +6065 } +6066 +6067 let pns = space.for_path(path_id); +6068 let ranges = pns.pending_acks.ranges(); +6069 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); +6070 let ecn = if receiving_ecn { +6071 Some(&pns.ecn_counters) +6072 } else { +6073 None +6074 }; +6075 +6076 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; +6077 // TODO: This should come from `TransportConfig` if that gets configurable. +6078 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; +6079 let delay = delay_micros >> ack_delay_exp.into_inner(); +6080 +6081 if is_multipath_negotiated && space_id == SpaceId::Data { +6082 if !ranges.is_empty() { +6083 let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn); +6084 builder.write_frame(frame, stats); +6085 } +6086 } else { +6087 builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats); +6088 } +6089 } +6090 +6091 fn close_common(&mut self) { +6092 trace!("connection closed"); +6093 self.timers.reset(); +6094 } +6095 +6096 fn set_close_timer(&mut self, now: Instant) { +6097 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of +6098 // all paths. +6099 let pto_max = self.max_pto_for_space(self.highest_space); +6100 self.timers.set( +6101 Timer::Conn(ConnTimer::Close), +6102 now + 3 * pto_max, +6103 self.qlog.with_time(now), +6104 ); +6105 } +6106 +6107 /// Handle transport parameters received from the peer +6108 /// +6109 /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the +6110 /// *packet into which the transport parameters arrived. +6111 fn handle_peer_params( +6112 &mut self, +6113 params: TransportParameters, +6114 local_cid: ConnectionId, +6115 remote_cid: ConnectionId, +6116 now: Instant, +6117 ) -> Result<(), TransportError> { +6118 if Some(self.original_remote_cid) != params.initial_src_cid +6119 || (self.side.is_client() +6120 && (Some(self.initial_dst_cid) != params.original_dst_cid +6121 || self.retry_src_cid != params.retry_src_cid)) +6122 { +6123 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( +6124 "CID authentication failure", +6125 )); +6126 } +6127 if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) { +6128 return Err(TransportError::PROTOCOL_VIOLATION( +6129 "multipath must not use zero-length CIDs", +6130 )); +6131 } +6132 +6133 self.set_peer_params(params); +6134 self.qlog.emit_peer_transport_params_received(self, now); 6135 -6136 fn close_common(&mut self) { -6137 trace!("connection closed"); -6138 self.timers.reset(); -6139 } -6140 -6141 fn set_close_timer(&mut self, now: Instant) { -6142 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of -6143 // all paths. -6144 let pto_max = self.max_pto_for_space(self.highest_space); -6145 self.timers.set( -6146 Timer::Conn(ConnTimer::Close), -6147 now + 3 * pto_max, -6148 self.qlog.with_time(now), -6149 ); -6150 } -6151 -6152 /// Handle transport parameters received from the peer -6153 /// -6154 /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the -6155 /// *packet into which the transport parameters arrived. -6156 fn handle_peer_params( -6157 &mut self, -6158 params: TransportParameters, -6159 local_cid: ConnectionId, -6160 remote_cid: ConnectionId, -6161 now: Instant, -6162 ) -> Result<(), TransportError> { -6163 if Some(self.original_remote_cid) != params.initial_src_cid -6164 || (self.side.is_client() -6165 && (Some(self.initial_dst_cid) != params.original_dst_cid -6166 || self.retry_src_cid != params.retry_src_cid)) -6167 { -6168 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( -6169 "CID authentication failure", -6170 )); -6171 } -6172 if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) { -6173 return Err(TransportError::PROTOCOL_VIOLATION( -6174 "multipath must not use zero-length CIDs", -6175 )); -6176 } -6177 -6178 self.set_peer_params(params); -6179 self.qlog.emit_peer_transport_params_received(self, now); -6180 -6181 Ok(()) -6182 } -6183 -6184 fn set_peer_params(&mut self, params: TransportParameters) { -6185 self.streams.set_params(&params); -6186 self.idle_timeout = -6187 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); -6188 trace!("negotiated max idle timeout {:?}", self.idle_timeout); -6189 -6190 if let Some(ref info) = params.preferred_address { -6191 // During the handshake PathId::ZERO exists. -6192 self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { -6193 path_id: None, -6194 sequence: 1, -6195 id: info.connection_id, -6196 reset_token: info.stateless_reset_token, -6197 retire_prior_to: 0, -6198 }) -6199 .expect( -6200 "preferred address CID is the first received, and hence is guaranteed to be legal", -6201 ); -6202 let remote = self.path_data(PathId::ZERO).network_path.remote; -6203 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); -6204 } -6205 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); -6206 -6207 let mut multipath_enabled = None; -6208 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( -6209 self.config.get_initial_max_path_id(), -6210 params.initial_max_path_id, -6211 ) { -6212 // multipath is enabled, register the local and remote maximums -6213 self.local_max_path_id = local_max_path_id; -6214 self.remote_max_path_id = remote_max_path_id; -6215 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); -6216 debug!(%initial_max_path_id, "multipath negotiated"); -6217 multipath_enabled = Some(initial_max_path_id); -6218 } -6219 -6220 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = -6221 self.config -6222 .max_remote_nat_traversal_addresses -6223 .zip(params.max_remote_nat_traversal_addresses) -6224 { -6225 if let Some(max_initial_paths) = -6226 multipath_enabled.map(|path_id| path_id.saturating_add(1u8)) -6227 { -6228 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); -6229 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); -6230 self.iroh_hp = -6231 iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side()); -6232 debug!( -6233 %max_remote_addresses, %max_local_addresses, -6234 "iroh hole punching negotiated" -6235 ); +6136 Ok(()) +6137 } +6138 +6139 fn set_peer_params(&mut self, params: TransportParameters) { +6140 self.streams.set_params(&params); +6141 self.idle_timeout = +6142 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); +6143 trace!("negotiated max idle timeout {:?}", self.idle_timeout); +6144 +6145 if let Some(ref info) = params.preferred_address { +6146 // During the handshake PathId::ZERO exists. +6147 self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { +6148 path_id: None, +6149 sequence: 1, +6150 id: info.connection_id, +6151 reset_token: info.stateless_reset_token, +6152 retire_prior_to: 0, +6153 }) +6154 .expect( +6155 "preferred address CID is the first received, and hence is guaranteed to be legal", +6156 ); +6157 let remote = self.path_data(PathId::ZERO).network_path.remote; +6158 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); +6159 } +6160 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); +6161 +6162 let mut multipath_enabled = None; +6163 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( +6164 self.config.get_initial_max_path_id(), +6165 params.initial_max_path_id, +6166 ) { +6167 // multipath is enabled, register the local and remote maximums +6168 self.local_max_path_id = local_max_path_id; +6169 self.remote_max_path_id = remote_max_path_id; +6170 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); +6171 debug!(%initial_max_path_id, "multipath negotiated"); +6172 multipath_enabled = Some(initial_max_path_id); +6173 } +6174 +6175 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = +6176 self.config +6177 .max_remote_nat_traversal_addresses +6178 .zip(params.max_remote_nat_traversal_addresses) +6179 { +6180 if let Some(max_initial_paths) = +6181 multipath_enabled.map(|path_id| path_id.saturating_add(1u8)) +6182 { +6183 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); +6184 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); +6185 self.iroh_hp = +6186 iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side()); +6187 debug!( +6188 %max_remote_addresses, %max_local_addresses, +6189 "iroh hole punching negotiated" +6190 ); +6191 +6192 match self.side() { +6193 Side::Client => { +6194 if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 { +6195 // in this case the client might try to open `max_remote_addresses` new +6196 // paths, but the current multipath configuration will not allow it +6197 warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues") +6198 } else if max_local_addresses as u64 +6199 > params.active_connection_id_limit.into_inner() +6200 { +6201 // the server allows us to send at most `params.active_connection_id_limit` +6202 // but they might need at least `max_local_addresses` to effectively send +6203 // `PATH_CHALLENGE` frames to each advertised local address +6204 warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues") +6205 } +6206 } +6207 Side::Server => { +6208 if (max_initial_paths.as_u32() as u64) < crate::LOCAL_CID_COUNT { +6209 warn!(%max_initial_paths, local_cid_limit=%crate::LOCAL_CID_COUNT, "local server configuration might cause nat traversal issues") +6210 } +6211 } +6212 } +6213 } else { +6214 debug!("iroh nat traversal enabled for both endpoints, but multipath is missing") +6215 } +6216 } +6217 +6218 self.peer_params = params; +6219 let peer_max_udp_payload_size = +6220 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); +6221 self.path_data_mut(PathId::ZERO) +6222 .mtud +6223 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); +6224 } +6225 +6226 /// Decrypts a packet, returning the packet number on success +6227 fn decrypt_packet( +6228 &mut self, +6229 now: Instant, +6230 path_id: PathId, +6231 packet: &mut Packet, +6232 ) -> Result<Option<u64>, Option<TransportError>> { +6233 let result = self +6234 .crypto_state +6235 .decrypt_packet_body(packet, path_id, &self.spaces)?; 6236 -6237 match self.side() { -6238 Side::Client => { -6239 if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 { -6240 // in this case the client might try to open `max_remote_addresses` new -6241 // paths, but the current multipath configuration will not allow it -6242 warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues") -6243 } else if max_local_addresses as u64 -6244 > params.active_connection_id_limit.into_inner() -6245 { -6246 // the server allows us to send at most `params.active_connection_id_limit` -6247 // but they might need at least `max_local_addresses` to effectively send -6248 // `PATH_CHALLENGE` frames to each advertised local address -6249 warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues") -6250 } -6251 } -6252 Side::Server => { -6253 if (max_initial_paths.as_u32() as u64) < crate::LOCAL_CID_COUNT { -6254 warn!(%max_initial_paths, local_cid_limit=%crate::LOCAL_CID_COUNT, "local server configuration might cause nat traversal issues") -6255 } -6256 } -6257 } -6258 } else { -6259 debug!("iroh nat traversal enabled for both endpoints, but multipath is missing") -6260 } -6261 } +6237 let result = match result { +6238 Some(r) => r, +6239 None => return Ok(None), +6240 }; +6241 +6242 if result.outgoing_key_update_acked +6243 && let Some(prev) = self.crypto_state.prev_crypto.as_mut() +6244 { +6245 prev.end_packet = Some((result.number, now)); +6246 self.set_key_discard_timer(now, packet.header.space()); +6247 } +6248 +6249 if result.incoming_key_update { +6250 trace!("key update authenticated"); +6251 self.crypto_state +6252 .update_keys(Some((result.number, now)), true); +6253 self.set_key_discard_timer(now, packet.header.space()); +6254 } +6255 +6256 Ok(Some(result.number)) +6257 } +6258 +6259 fn peer_supports_ack_frequency(&self) -> bool { +6260 self.peer_params.min_ack_delay.is_some() +6261 } 6262 -6263 self.peer_params = params; -6264 let peer_max_udp_payload_size = -6265 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); -6266 self.path_data_mut(PathId::ZERO) -6267 .mtud -6268 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); -6269 } -6270 -6271 /// Decrypts a packet, returning the packet number on success -6272 fn decrypt_packet( -6273 &mut self, -6274 now: Instant, -6275 path_id: PathId, -6276 packet: &mut Packet, -6277 ) -> Result<Option<u64>, Option<TransportError>> { -6278 let result = self -6279 .crypto_state -6280 .decrypt_packet_body(packet, path_id, &self.spaces)?; -6281 -6282 let result = match result { -6283 Some(r) => r, -6284 None => return Ok(None), -6285 }; -6286 -6287 if result.outgoing_key_update_acked -6288 && let Some(prev) = self.crypto_state.prev_crypto.as_mut() -6289 { -6290 prev.end_packet = Some((result.number, now)); -6291 self.set_key_discard_timer(now, packet.header.space()); -6292 } -6293 -6294 if result.incoming_key_update { -6295 trace!("key update authenticated"); -6296 self.crypto_state -6297 .update_keys(Some((result.number, now)), true); -6298 self.set_key_discard_timer(now, packet.header.space()); -6299 } -6300 -6301 Ok(Some(result.number)) -6302 } +6263 /// Send an IMMEDIATE_ACK frame to the remote endpoint +6264 /// +6265 /// According to the spec, this will result in an error if the remote endpoint does not support +6266 /// the Acknowledgement Frequency extension +6267 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { +6268 debug_assert_eq!( +6269 self.highest_space, +6270 SpaceKind::Data, +6271 "immediate ack must be written in the data space" +6272 ); +6273 self.spaces[SpaceId::Data] +6274 .for_path(path_id) +6275 .immediate_ack_pending = true; +6276 } +6277 +6278 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests +6279 #[cfg(test)] +6280 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { +6281 let (path_id, first_decode, remaining) = match &event.0 { +6282 ConnectionEventInner::Datagram(DatagramConnectionEvent { +6283 path_id, +6284 first_decode, +6285 remaining, +6286 .. +6287 }) => (path_id, first_decode, remaining), +6288 _ => return None, +6289 }; +6290 +6291 if remaining.is_some() { +6292 panic!("Packets should never be coalesced in tests"); +6293 } +6294 +6295 let decrypted_header = self +6296 .crypto_state +6297 .unprotect_header(first_decode.clone(), self.peer_params.stateless_reset_token)?; +6298 +6299 let mut packet = decrypted_header.packet?; +6300 self.crypto_state +6301 .decrypt_packet_body(&mut packet, *path_id, &self.spaces) +6302 .ok()?; 6303 -6304 fn peer_supports_ack_frequency(&self) -> bool { -6305 self.peer_params.min_ack_delay.is_some() -6306 } -6307 -6308 /// Send an IMMEDIATE_ACK frame to the remote endpoint -6309 /// -6310 /// According to the spec, this will result in an error if the remote endpoint does not support -6311 /// the Acknowledgement Frequency extension -6312 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { -6313 debug_assert_eq!( -6314 self.highest_space, -6315 SpaceKind::Data, -6316 "immediate ack must be written in the data space" -6317 ); -6318 self.spaces[SpaceId::Data] -6319 .for_path(path_id) -6320 .immediate_ack_pending = true; -6321 } -6322 -6323 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests -6324 #[cfg(test)] -6325 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { -6326 let (path_id, first_decode, remaining) = match &event.0 { -6327 ConnectionEventInner::Datagram(DatagramConnectionEvent { -6328 path_id, -6329 first_decode, -6330 remaining, -6331 .. -6332 }) => (path_id, first_decode, remaining), -6333 _ => return None, -6334 }; -6335 -6336 if remaining.is_some() { -6337 panic!("Packets should never be coalesced in tests"); -6338 } -6339 -6340 let decrypted_header = self -6341 .crypto_state -6342 .unprotect_header(first_decode.clone(), self.peer_params.stateless_reset_token)?; -6343 -6344 let mut packet = decrypted_header.packet?; -6345 self.crypto_state -6346 .decrypt_packet_body(&mut packet, *path_id, &self.spaces) -6347 .ok()?; +6304 Some(packet.payload.to_vec()) +6305 } +6306 +6307 /// The number of bytes of packets containing retransmittable frames that have not been +6308 /// acknowledged or declared lost. +6309 #[cfg(test)] +6310 pub(crate) fn bytes_in_flight(&self) -> u64 { +6311 // TODO(@divma): consider including for multipath? +6312 self.path_data(PathId::ZERO).in_flight.bytes +6313 } +6314 +6315 /// Number of bytes worth of non-ack-only packets that may be sent +6316 #[cfg(test)] +6317 pub(crate) fn congestion_window(&self) -> u64 { +6318 let path = self.path_data(PathId::ZERO); +6319 path.congestion +6320 .window() +6321 .saturating_sub(path.in_flight.bytes) +6322 } +6323 +6324 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running +6325 #[cfg(test)] +6326 pub(crate) fn is_idle(&self) -> bool { +6327 let current_timers = self.timers.values(); +6328 current_timers +6329 .into_iter() +6330 .filter(|(timer, _)| { +6331 !matches!( +6332 timer, +6333 Timer::Conn(ConnTimer::KeepAlive) +6334 | Timer::PerPath(_, PathTimer::PathKeepAlive) +6335 | Timer::Conn(ConnTimer::PushNewCid) +6336 | Timer::Conn(ConnTimer::KeyDiscard) +6337 ) +6338 }) +6339 .min_by_key(|(_, time)| *time) +6340 .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle)) +6341 } +6342 +6343 /// Whether explicit congestion notification is in use on outgoing packets. +6344 #[cfg(test)] +6345 pub(crate) fn using_ecn(&self) -> bool { +6346 self.path_data(PathId::ZERO).sending_ecn +6347 } 6348 -6349 Some(packet.payload.to_vec()) -6350 } -6351 -6352 /// The number of bytes of packets containing retransmittable frames that have not been -6353 /// acknowledged or declared lost. -6354 #[cfg(test)] -6355 pub(crate) fn bytes_in_flight(&self) -> u64 { -6356 // TODO(@divma): consider including for multipath? -6357 self.path_data(PathId::ZERO).in_flight.bytes -6358 } -6359 -6360 /// Number of bytes worth of non-ack-only packets that may be sent -6361 #[cfg(test)] -6362 pub(crate) fn congestion_window(&self) -> u64 { -6363 let path = self.path_data(PathId::ZERO); -6364 path.congestion -6365 .window() -6366 .saturating_sub(path.in_flight.bytes) -6367 } -6368 -6369 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running -6370 #[cfg(test)] -6371 pub(crate) fn is_idle(&self) -> bool { -6372 let current_timers = self.timers.values(); -6373 current_timers -6374 .into_iter() -6375 .filter(|(timer, _)| { -6376 !matches!( -6377 timer, -6378 Timer::Conn(ConnTimer::KeepAlive) -6379 | Timer::PerPath(_, PathTimer::PathKeepAlive) -6380 | Timer::Conn(ConnTimer::PushNewCid) -6381 | Timer::Conn(ConnTimer::KeyDiscard) -6382 ) -6383 }) -6384 .min_by_key(|(_, time)| *time) -6385 .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle)) -6386 } -6387 -6388 /// Whether explicit congestion notification is in use on outgoing packets. -6389 #[cfg(test)] -6390 pub(crate) fn using_ecn(&self) -> bool { -6391 self.path_data(PathId::ZERO).sending_ecn -6392 } -6393 -6394 /// The number of received bytes in the current path -6395 #[cfg(test)] -6396 pub(crate) fn total_recvd(&self) -> u64 { -6397 self.path_data(PathId::ZERO).total_recvd -6398 } -6399 -6400 #[cfg(test)] -6401 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { -6402 self.local_cid_state -6403 .get(&PathId::ZERO) -6404 .unwrap() -6405 .active_seq() -6406 } -6407 -6408 #[cfg(test)] -6409 #[track_caller] -6410 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { -6411 self.local_cid_state -6412 .get(&PathId(path_id)) -6413 .unwrap() -6414 .active_seq() -6415 } -6416 -6417 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame -6418 /// with updated `retire_prior_to` field set to `v` -6419 #[cfg(test)] -6420 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { -6421 let n = self -6422 .local_cid_state -6423 .get_mut(&PathId::ZERO) -6424 .unwrap() -6425 .assign_retire_seq(v); -6426 self.endpoint_events -6427 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -6428 } -6429 -6430 /// Check the current active remote CID sequence for `PathId::ZERO` -6431 #[cfg(test)] -6432 pub(crate) fn active_remote_cid_seq(&self) -> u64 { -6433 self.remote_cids.get(&PathId::ZERO).unwrap().active_seq() -6434 } -6435 -6436 /// Returns the detected maximum udp payload size for the current path -6437 #[cfg(test)] -6438 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { -6439 self.path_data(path_id).current_mtu() -6440 } -6441 -6442 /// Triggers path validation on all paths -6443 #[cfg(test)] -6444 pub(crate) fn trigger_path_validation(&mut self) { -6445 for path in self.paths.values_mut() { -6446 path.data.send_new_challenge = true; -6447 } -6448 } -6449 -6450 /// Whether we have 1-RTT data to send -6451 /// -6452 /// This checks for frames that can only be sent in the data space (1-RTT): -6453 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. -6454 /// - Pending PATH_RESPONSE frames. -6455 /// - Pending data to send in STREAM frames. -6456 /// - Pending DATAGRAM frames to send. -6457 /// -6458 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that -6459 /// may need to be sent. -6460 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { -6461 let path_exclusive = self.paths.get(&path_id).is_some_and(|path| { -6462 path.data.send_new_challenge -6463 || path -6464 .prev -6465 .as_ref() -6466 .is_some_and(|(_, path)| path.send_new_challenge) -6467 || !path.data.path_responses.is_empty() -6468 }); -6469 let other = self.streams.can_send_stream_data() -6470 || self -6471 .datagrams -6472 .outgoing -6473 .front() -6474 .is_some_and(|x| x.size(true) <= max_size); -6475 SendableFrames { -6476 acks: false, -6477 other, -6478 close: false, -6479 path_exclusive, -6480 } -6481 } -6482 -6483 /// Terminate the connection instantly, without sending a close packet -6484 fn kill(&mut self, reason: ConnectionError) { -6485 self.close_common(); -6486 self.state.move_to_drained(Some(reason)); -6487 self.endpoint_events.push_back(EndpointEventInner::Drained); -6488 } -6489 -6490 /// Storage size required for the largest packet that can be transmitted on all currently -6491 /// available paths -6492 /// -6493 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. -6494 /// -6495 /// When multipath is enabled, this value is the minimum MTU across all available paths. -6496 pub fn current_mtu(&self) -> u16 { -6497 self.paths -6498 .iter() -6499 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) -6500 .map(|(_path_id, path_state)| path_state.data.current_mtu()) -6501 .min() -6502 .expect("There is always at least one available path") -6503 } -6504 -6505 /// Size of non-frame data for a 1-RTT packet -6506 /// -6507 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are -6508 /// frames. Changes if the length of the remote connection ID changes, which is expected to be -6509 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in -6510 /// latency and packet loss. -6511 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { -6512 let pn_len = PacketNumber::new( -6513 pn, -6514 self.spaces[SpaceId::Data] -6515 .for_path(path) -6516 .largest_acked_packet -6517 .unwrap_or(0), -6518 ) -6519 .len(); -6520 -6521 // 1 byte for flags -6522 1 + self -6523 .remote_cids -6524 .get(&path) -6525 .map(|cids| cids.active().len()) -6526 .unwrap_or(20) // Max CID len in QUIC v1 -6527 + pn_len -6528 + self.tag_len_1rtt() -6529 } -6530 -6531 fn predict_1rtt_overhead_no_pn(&self) -> usize { -6532 let pn_len = 4; -6533 -6534 let cid_len = self -6535 .remote_cids -6536 .values() -6537 .map(|cids| cids.active().len()) -6538 .max() -6539 .unwrap_or(20); // Max CID len in QUIC v1 -6540 -6541 // 1 byte for flags -6542 1 + cid_len + pn_len + self.tag_len_1rtt() -6543 } -6544 -6545 fn tag_len_1rtt(&self) -> usize { -6546 // encryption_keys for Data space returns 1-RTT keys if available, otherwise 0-RTT keys -6547 let packet_crypto = self -6548 .crypto_state -6549 .encryption_keys(SpaceKind::Data, self.side.side()) -6550 .map(|(_header, packet, _level)| packet); -6551 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of -6552 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, -6553 // but that would needlessly prevent sending datagrams during 0-RTT. -6554 packet_crypto.map_or(16, |x| x.tag_len()) -6555 } -6556 -6557 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate -6558 fn on_path_validated(&mut self, path_id: PathId) { -6559 self.path_data_mut(path_id).validated = true; -6560 let ConnectionSide::Server { server_config } = &self.side else { -6561 return; -6562 }; -6563 let network_path = self.path_data(path_id).network_path; -6564 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; -6565 new_tokens.clear(); -6566 for _ in 0..server_config.validation_token.sent { -6567 new_tokens.push(network_path); -6568 } -6569 } -6570 -6571 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP -6572 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { -6573 if let Some(path) = self.paths.get_mut(&path_id) { -6574 path.data.status.remote_update(status, status_seq_no); -6575 } else { -6576 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); -6577 } -6578 self.events.push_back( -6579 PathEvent::RemoteStatus { -6580 id: path_id, -6581 status, -6582 } -6583 .into(), -6584 ); -6585 } -6586 -6587 /// Returns the maximum [`PathId`] to be used for sending in this connection. -6588 /// -6589 /// This is calculated as minimum between the local and remote's maximums when multipath is -6590 /// enabled, or `None` when disabled. -6591 /// -6592 /// For data that's received, we should use [`Self::local_max_path_id`] instead. -6593 /// The reasoning is that the remote might already have updated to its own newer -6594 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. -6595 fn max_path_id(&self) -> Option<PathId> { -6596 if self.is_multipath_negotiated() { -6597 Some(self.remote_max_path_id.min(self.local_max_path_id)) -6598 } else { -6599 None -6600 } -6601 } -6602 -6603 /// Returns whether this connection has a socket that supports IPv6. -6604 /// -6605 /// TODO(matheus23): This is related to quinn endpoint state's `ipv6` bool. We should move that info -6606 /// here instead of trying to hack around not knowing it exactly. -6607 fn is_ipv6(&self) -> bool { -6608 self.paths -6609 .values() -6610 .any(|p| p.data.network_path.remote.is_ipv6()) -6611 } -6612 -6613 /// Add addresses the local endpoint considers are reachable for nat traversal. -6614 pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> { -6615 if let Some(added) = self.iroh_hp.add_local_address(address)? { -6616 self.spaces[SpaceId::Data].pending.add_address.insert(added); -6617 }; -6618 Ok(()) -6619 } -6620 -6621 /// Removes an address the endpoing no longer considers reachable for nat traversal -6622 /// -6623 /// Addresses not present in the set will be silently ignored. -6624 pub fn remove_nat_traversal_address( -6625 &mut self, -6626 address: SocketAddr, -6627 ) -> Result<(), iroh_hp::Error> { -6628 if let Some(removed) = self.iroh_hp.remove_local_address(address)? { -6629 self.spaces[SpaceId::Data] -6630 .pending -6631 .remove_address -6632 .insert(removed); +6349 /// The number of received bytes in the current path +6350 #[cfg(test)] +6351 pub(crate) fn total_recvd(&self) -> u64 { +6352 self.path_data(PathId::ZERO).total_recvd +6353 } +6354 +6355 #[cfg(test)] +6356 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { +6357 self.local_cid_state +6358 .get(&PathId::ZERO) +6359 .unwrap() +6360 .active_seq() +6361 } +6362 +6363 #[cfg(test)] +6364 #[track_caller] +6365 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { +6366 self.local_cid_state +6367 .get(&PathId(path_id)) +6368 .unwrap() +6369 .active_seq() +6370 } +6371 +6372 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame +6373 /// with updated `retire_prior_to` field set to `v` +6374 #[cfg(test)] +6375 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { +6376 let n = self +6377 .local_cid_state +6378 .get_mut(&PathId::ZERO) +6379 .unwrap() +6380 .assign_retire_seq(v); +6381 self.endpoint_events +6382 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +6383 } +6384 +6385 /// Check the current active remote CID sequence for `PathId::ZERO` +6386 #[cfg(test)] +6387 pub(crate) fn active_remote_cid_seq(&self) -> u64 { +6388 self.remote_cids.get(&PathId::ZERO).unwrap().active_seq() +6389 } +6390 +6391 /// Returns the detected maximum udp payload size for the current path +6392 #[cfg(test)] +6393 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { +6394 self.path_data(path_id).current_mtu() +6395 } +6396 +6397 /// Triggers path validation on all paths +6398 #[cfg(test)] +6399 pub(crate) fn trigger_path_validation(&mut self) { +6400 for path in self.paths.values_mut() { +6401 path.data.send_new_challenge = true; +6402 } +6403 } +6404 +6405 /// Whether we have 1-RTT data to send +6406 /// +6407 /// This checks for frames that can only be sent in the data space (1-RTT): +6408 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. +6409 /// - Pending PATH_RESPONSE frames. +6410 /// - Pending data to send in STREAM frames. +6411 /// - Pending DATAGRAM frames to send. +6412 /// +6413 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that +6414 /// may need to be sent. +6415 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { +6416 let path_exclusive = self.paths.get(&path_id).is_some_and(|path| { +6417 path.data.send_new_challenge +6418 || path +6419 .prev +6420 .as_ref() +6421 .is_some_and(|(_, path)| path.send_new_challenge) +6422 || !path.data.path_responses.is_empty() +6423 }); +6424 let other = self.streams.can_send_stream_data() +6425 || self +6426 .datagrams +6427 .outgoing +6428 .front() +6429 .is_some_and(|x| x.size(true) <= max_size); +6430 SendableFrames { +6431 acks: false, +6432 other, +6433 close: false, +6434 path_exclusive, +6435 } +6436 } +6437 +6438 /// Terminate the connection instantly, without sending a close packet +6439 fn kill(&mut self, reason: ConnectionError) { +6440 self.close_common(); +6441 self.state.move_to_drained(Some(reason)); +6442 self.endpoint_events.push_back(EndpointEventInner::Drained); +6443 } +6444 +6445 /// Storage size required for the largest packet that can be transmitted on all currently +6446 /// available paths +6447 /// +6448 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. +6449 /// +6450 /// When multipath is enabled, this value is the minimum MTU across all available paths. +6451 pub fn current_mtu(&self) -> u16 { +6452 self.paths +6453 .iter() +6454 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) +6455 .map(|(_path_id, path_state)| path_state.data.current_mtu()) +6456 .min() +6457 .expect("There is always at least one available path") +6458 } +6459 +6460 /// Size of non-frame data for a 1-RTT packet +6461 /// +6462 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are +6463 /// frames. Changes if the length of the remote connection ID changes, which is expected to be +6464 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in +6465 /// latency and packet loss. +6466 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { +6467 let pn_len = PacketNumber::new( +6468 pn, +6469 self.spaces[SpaceId::Data] +6470 .for_path(path) +6471 .largest_acked_packet +6472 .unwrap_or(0), +6473 ) +6474 .len(); +6475 +6476 // 1 byte for flags +6477 1 + self +6478 .remote_cids +6479 .get(&path) +6480 .map(|cids| cids.active().len()) +6481 .unwrap_or(20) // Max CID len in QUIC v1 +6482 + pn_len +6483 + self.tag_len_1rtt() +6484 } +6485 +6486 fn predict_1rtt_overhead_no_pn(&self) -> usize { +6487 let pn_len = 4; +6488 +6489 let cid_len = self +6490 .remote_cids +6491 .values() +6492 .map(|cids| cids.active().len()) +6493 .max() +6494 .unwrap_or(20); // Max CID len in QUIC v1 +6495 +6496 // 1 byte for flags +6497 1 + cid_len + pn_len + self.tag_len_1rtt() +6498 } +6499 +6500 fn tag_len_1rtt(&self) -> usize { +6501 // encryption_keys for Data space returns 1-RTT keys if available, otherwise 0-RTT keys +6502 let packet_crypto = self +6503 .crypto_state +6504 .encryption_keys(SpaceKind::Data, self.side.side()) +6505 .map(|(_header, packet, _level)| packet); +6506 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of +6507 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, +6508 // but that would needlessly prevent sending datagrams during 0-RTT. +6509 packet_crypto.map_or(16, |x| x.tag_len()) +6510 } +6511 +6512 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate +6513 fn on_path_validated(&mut self, path_id: PathId) { +6514 self.path_data_mut(path_id).validated = true; +6515 let ConnectionSide::Server { server_config } = &self.side else { +6516 return; +6517 }; +6518 let network_path = self.path_data(path_id).network_path; +6519 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; +6520 new_tokens.clear(); +6521 for _ in 0..server_config.validation_token.sent { +6522 new_tokens.push(network_path); +6523 } +6524 } +6525 +6526 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP +6527 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { +6528 if let Some(path) = self.paths.get_mut(&path_id) { +6529 path.data.status.remote_update(status, status_seq_no); +6530 } else { +6531 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); +6532 } +6533 self.events.push_back( +6534 PathEvent::RemoteStatus { +6535 id: path_id, +6536 status, +6537 } +6538 .into(), +6539 ); +6540 } +6541 +6542 /// Returns the maximum [`PathId`] to be used for sending in this connection. +6543 /// +6544 /// This is calculated as minimum between the local and remote's maximums when multipath is +6545 /// enabled, or `None` when disabled. +6546 /// +6547 /// For data that's received, we should use [`Self::local_max_path_id`] instead. +6548 /// The reasoning is that the remote might already have updated to its own newer +6549 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. +6550 fn max_path_id(&self) -> Option<PathId> { +6551 if self.is_multipath_negotiated() { +6552 Some(self.remote_max_path_id.min(self.local_max_path_id)) +6553 } else { +6554 None +6555 } +6556 } +6557 +6558 /// Returns whether this connection has a socket that supports IPv6. +6559 /// +6560 /// TODO(matheus23): This is related to quinn endpoint state's `ipv6` bool. We should move that info +6561 /// here instead of trying to hack around not knowing it exactly. +6562 fn is_ipv6(&self) -> bool { +6563 self.paths +6564 .values() +6565 .any(|p| p.data.network_path.remote.is_ipv6()) +6566 } +6567 +6568 /// Add addresses the local endpoint considers are reachable for nat traversal. +6569 pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> { +6570 if let Some(added) = self.iroh_hp.add_local_address(address)? { +6571 self.spaces[SpaceId::Data].pending.add_address.insert(added); +6572 }; +6573 Ok(()) +6574 } +6575 +6576 /// Removes an address the endpoing no longer considers reachable for nat traversal +6577 /// +6578 /// Addresses not present in the set will be silently ignored. +6579 pub fn remove_nat_traversal_address( +6580 &mut self, +6581 address: SocketAddr, +6582 ) -> Result<(), iroh_hp::Error> { +6583 if let Some(removed) = self.iroh_hp.remove_local_address(address)? { +6584 self.spaces[SpaceId::Data] +6585 .pending +6586 .remove_address +6587 .insert(removed); +6588 } +6589 Ok(()) +6590 } +6591 +6592 /// Get the current local nat traversal addresses +6593 pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6594 self.iroh_hp.get_local_nat_traversal_addresses() +6595 } +6596 +6597 /// Get the currently advertised nat traversal addresses by the server +6598 pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6599 Ok(self +6600 .iroh_hp +6601 .client_side()? +6602 .get_remote_nat_traversal_addresses()) +6603 } +6604 +6605 /// Attempts to open a path for nat traversal. +6606 /// +6607 /// On success returns the [`PathId`] and remote address of the path. +6608 fn open_nat_traversal_path( +6609 &mut self, +6610 now: Instant, +6611 ip_port: (IpAddr, u16), +6612 ) -> Result<Option<(PathId, SocketAddr)>, PathError> { +6613 let remote = ip_port.into(); +6614 // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address? +6615 // By specifying None for `local_ip`, we do two things: 1. open_path_ensure won't +6616 // generate two paths to the same remote and 2. we let the OS choose which +6617 // interface to use for sending on that path. +6618 let network_path = FourTuple { +6619 remote, +6620 local_ip: None, +6621 }; +6622 match self.open_path_ensure(network_path, PathStatus::Backup, now) { +6623 Ok((path_id, path_was_known)) => { +6624 if path_was_known { +6625 trace!(%path_id, %remote, "nat traversal: path existed for remote"); +6626 } +6627 Ok(Some((path_id, remote))) +6628 } +6629 Err(e) => { +6630 debug!(%remote, %e, "nat traversal: failed to probe remote"); +6631 Err(e) +6632 } 6633 } -6634 Ok(()) -6635 } -6636 -6637 /// Get the current local nat traversal addresses -6638 pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6639 self.iroh_hp.get_local_nat_traversal_addresses() -6640 } -6641 -6642 /// Get the currently advertised nat traversal addresses by the server -6643 pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6644 Ok(self -6645 .iroh_hp -6646 .client_side()? -6647 .get_remote_nat_traversal_addresses()) -6648 } -6649 -6650 /// Attempts to open a path for nat traversal. -6651 /// -6652 /// On success returns the [`PathId`] and remote address of the path. -6653 fn open_nat_traversal_path( -6654 &mut self, -6655 now: Instant, -6656 ip_port: (IpAddr, u16), -6657 ) -> Result<Option<(PathId, SocketAddr)>, PathError> { -6658 let remote = ip_port.into(); -6659 // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address? -6660 // By specifying None for `local_ip`, we do two things: 1. open_path_ensure won't -6661 // generate two paths to the same remote and 2. we let the OS choose which -6662 // interface to use for sending on that path. -6663 let network_path = FourTuple { -6664 remote, -6665 local_ip: None, -6666 }; -6667 match self.open_path_ensure(network_path, PathStatus::Backup, now) { -6668 Ok((path_id, path_was_known)) => { -6669 if path_was_known { -6670 trace!(%path_id, %remote, "nat traversal: path existed for remote"); -6671 } -6672 Ok(Some((path_id, remote))) -6673 } -6674 Err(e) => { -6675 debug!(%remote, %e, "nat traversal: failed to probe remote"); -6676 Err(e) -6677 } -6678 } -6679 } -6680 -6681 /// Initiates a new nat traversal round -6682 /// -6683 /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT` -6684 /// frames, and initiating probing of the known remote addresses. When a new round is -6685 /// initiated, the previous one is cancelled, and paths that have not been opened are closed. -6686 /// -6687 /// Returns the server addresses that are now being probed. -6688 /// If addresses fail due to spurious errors, these might succeed later and not be returned in -6689 /// this set. -6690 pub fn initiate_nat_traversal_round( -6691 &mut self, -6692 now: Instant, -6693 ) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6694 if self.state.is_closed() { -6695 return Err(iroh_hp::Error::Closed); -6696 } -6697 -6698 let ipv6 = self.is_ipv6(); -6699 let client_state = self.iroh_hp.client_side_mut()?; -6700 let iroh_hp::NatTraversalRound { -6701 new_round, -6702 reach_out_at, -6703 addresses_to_probe, -6704 prev_round_path_ids, -6705 } = client_state.initiate_nat_traversal_round(ipv6)?; -6706 -6707 trace!(%new_round, reach_out=reach_out_at.len(), to_probe=addresses_to_probe.len(), -6708 "initiating nat traversal round"); -6709 -6710 self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at)); -6711 -6712 for path_id in prev_round_path_ids { -6713 let Some(path) = self.path(path_id) else { -6714 continue; -6715 }; -6716 let ip = path.network_path.remote.ip(); -6717 let port = path.network_path.remote.port(); -6718 -6719 // We only close paths that aren't validated (thus are working) that we opened -6720 // in a previous round. -6721 // And we only close paths that we don't want to probe anyways. -6722 if !addresses_to_probe -6723 .iter() -6724 .any(|(_, probe)| *probe == (ip, port)) -6725 && !path.validated -6726 && !self.abandoned_paths.contains(&path_id) -6727 { -6728 trace!(%path_id, "closing path from previous round"); -6729 let _ = self.close_path( -6730 now, -6731 path_id, -6732 TransportErrorCode::APPLICATION_ABANDON_PATH.into(), -6733 ); -6734 } -6735 } -6736 -6737 let mut err = None; -6738 -6739 let mut path_ids = Vec::with_capacity(addresses_to_probe.len()); -6740 let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len()); -6741 -6742 for (id, address) in addresses_to_probe { -6743 match self.open_nat_traversal_path(now, address) { -6744 Ok(None) => {} -6745 Ok(Some((path_id, remote))) => { -6746 path_ids.push(path_id); -6747 probed_addresses.push(remote); -6748 } -6749 Err(e) => { -6750 self.iroh_hp -6751 .client_side_mut() -6752 .expect("validated") -6753 .report_in_continuation(id, e); -6754 err.get_or_insert(e); -6755 } -6756 } -6757 } -6758 -6759 if let Some(err) = err { -6760 // We failed to probe any addresses, bail out -6761 if probed_addresses.is_empty() { -6762 return Err(iroh_hp::Error::Multipath(err)); -6763 } -6764 } +6634 } +6635 +6636 /// Initiates a new nat traversal round +6637 /// +6638 /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT` +6639 /// frames, and initiating probing of the known remote addresses. When a new round is +6640 /// initiated, the previous one is cancelled, and paths that have not been opened are closed. +6641 /// +6642 /// Returns the server addresses that are now being probed. +6643 /// If addresses fail due to spurious errors, these might succeed later and not be returned in +6644 /// this set. +6645 pub fn initiate_nat_traversal_round( +6646 &mut self, +6647 now: Instant, +6648 ) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6649 if self.state.is_closed() { +6650 return Err(iroh_hp::Error::Closed); +6651 } +6652 +6653 let ipv6 = self.is_ipv6(); +6654 let client_state = self.iroh_hp.client_side_mut()?; +6655 let iroh_hp::NatTraversalRound { +6656 new_round, +6657 reach_out_at, +6658 addresses_to_probe, +6659 prev_round_path_ids, +6660 } = client_state.initiate_nat_traversal_round(ipv6)?; +6661 +6662 trace!(%new_round, reach_out=reach_out_at.len(), to_probe=addresses_to_probe.len(), +6663 "initiating nat traversal round"); +6664 +6665 self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at)); +6666 +6667 for path_id in prev_round_path_ids { +6668 let Some(path) = self.path(path_id) else { +6669 continue; +6670 }; +6671 let ip = path.network_path.remote.ip(); +6672 let port = path.network_path.remote.port(); +6673 +6674 // We only close paths that aren't validated (thus are working) that we opened +6675 // in a previous round. +6676 // And we only close paths that we don't want to probe anyways. +6677 if !addresses_to_probe +6678 .iter() +6679 .any(|(_, probe)| *probe == (ip, port)) +6680 && !path.validated +6681 && !self.abandoned_paths.contains(&path_id) +6682 { +6683 trace!(%path_id, "closing path from previous round"); +6684 let _ = self.close_path( +6685 now, +6686 path_id, +6687 TransportErrorCode::APPLICATION_ABANDON_PATH.into(), +6688 ); +6689 } +6690 } +6691 +6692 let mut err = None; +6693 +6694 let mut path_ids = Vec::with_capacity(addresses_to_probe.len()); +6695 let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len()); +6696 +6697 for (id, address) in addresses_to_probe { +6698 match self.open_nat_traversal_path(now, address) { +6699 Ok(None) => {} +6700 Ok(Some((path_id, remote))) => { +6701 path_ids.push(path_id); +6702 probed_addresses.push(remote); +6703 } +6704 Err(e) => { +6705 self.iroh_hp +6706 .client_side_mut() +6707 .expect("validated") +6708 .report_in_continuation(id, e); +6709 err.get_or_insert(e); +6710 } +6711 } +6712 } +6713 +6714 if let Some(err) = err { +6715 // We failed to probe any addresses, bail out +6716 if probed_addresses.is_empty() { +6717 return Err(iroh_hp::Error::Multipath(err)); +6718 } +6719 } +6720 +6721 self.iroh_hp +6722 .client_side_mut() +6723 .expect("connection side validated") +6724 .set_round_path_ids(path_ids); +6725 +6726 Ok(probed_addresses) +6727 } +6728 +6729 /// Attempts to continue a nat traversal round by trying to open paths for pending client probes. +6730 /// +6731 /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was +6732 /// successfully open. +6733 fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> { +6734 let ipv6 = self.is_ipv6(); +6735 let client_state = self.iroh_hp.client_side_mut().ok()?; +6736 let (id, address) = client_state.continue_nat_traversal_round(ipv6)?; +6737 let open_result = self.open_nat_traversal_path(now, address); +6738 let client_state = self.iroh_hp.client_side_mut().expect("validated"); +6739 match open_result { +6740 Ok(None) => Some(true), +6741 Ok(Some((path_id, _remote))) => { +6742 client_state.add_round_path_id(path_id); +6743 Some(true) +6744 } +6745 Err(e) => { +6746 client_state.report_in_continuation(id, e); +6747 Some(false) +6748 } +6749 } +6750 } +6751} +6752 +6753/// Hints when the caller identifies a network change. +6754pub trait NetworkChangeHint: std::fmt::Debug + 'static { +6755 /// Inform the connection if a path may recover after a network change. +6756 /// +6757 /// After network changes, paths may not be recoverable. In this case, waiting for the path to +6758 /// become idle may take longer than what is desirable. If [`Self::is_path_recoverable`] +6759 /// returns `false`, a multipath-enabled, client-side connection will establish a new path to +6760 /// the same remote, closing the current one, instead of migrating the path. +6761 /// +6762 /// Paths that are deemed recoverable will simply be sent a PING for a liveness check. +6763 fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool; +6764} 6765 -6766 self.iroh_hp -6767 .client_side_mut() -6768 .expect("connection side validated") -6769 .set_round_path_ids(path_ids); -6770 -6771 Ok(probed_addresses) -6772 } +6766impl fmt::Debug for Connection { +6767 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +6768 f.debug_struct("Connection") +6769 .field("handshake_cid", &self.handshake_cid) +6770 .finish() +6771 } +6772} 6773 -6774 /// Attempts to continue a nat traversal round by trying to open paths for pending client probes. -6775 /// -6776 /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was -6777 /// successfully open. -6778 fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> { -6779 let ipv6 = self.is_ipv6(); -6780 let client_state = self.iroh_hp.client_side_mut().ok()?; -6781 let (id, address) = client_state.continue_nat_traversal_round(ipv6)?; -6782 let open_result = self.open_nat_traversal_path(now, address); -6783 let client_state = self.iroh_hp.client_side_mut().expect("validated"); -6784 match open_result { -6785 Ok(None) => Some(true), -6786 Ok(Some((path_id, _remote))) => { -6787 client_state.add_round_path_id(path_id); -6788 Some(true) -6789 } -6790 Err(e) => { -6791 client_state.report_in_continuation(id, e); -6792 Some(false) -6793 } -6794 } -6795 } -6796} -6797 -6798/// Hints when the caller identifies a network change. -6799pub trait NetworkChangeHint: std::fmt::Debug + 'static { -6800 /// Inform the connection if a path may recover after a network change. -6801 /// -6802 /// After network changes, paths may not be recoverable. In this case, waiting for the path to -6803 /// become idle may take longer than what is desirable. If [`Self::is_path_recoverable`] -6804 /// returns `false`, a multipath-enabled, client-side connection will establish a new path to -6805 /// the same remote, closing the current one, instead of migrating the path. -6806 /// -6807 /// Paths that are deemed recoverable will simply be sent a PING for a liveness check. -6808 fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool; -6809} -6810 -6811impl fmt::Debug for Connection { -6812 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -6813 f.debug_struct("Connection") -6814 .field("handshake_cid", &self.handshake_cid) -6815 .finish() -6816 } -6817} -6818 -6819#[derive(Debug, Copy, Clone, PartialEq, Eq)] -6820enum PathBlocked { -6821 No, -6822 AntiAmplification, -6823 Congestion, -6824 Pacing, -6825} -6826 -6827/// Fields of `Connection` specific to it being client-side or server-side -6828enum ConnectionSide { -6829 Client { -6830 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded -6831 token: Bytes, -6832 token_store: Arc<dyn TokenStore>, -6833 server_name: String, -6834 }, -6835 Server { -6836 server_config: Arc<ServerConfig>, -6837 }, -6838} -6839 -6840impl ConnectionSide { -6841 fn remote_may_migrate(&self, state: &State) -> bool { -6842 match self { -6843 Self::Server { server_config } => server_config.migration, -6844 Self::Client { .. } => { -6845 if let Some(hs) = state.as_handshake() { -6846 hs.allow_server_migration -6847 } else { -6848 false -6849 } -6850 } -6851 } -6852 } -6853 -6854 fn is_client(&self) -> bool { -6855 self.side().is_client() -6856 } +6774#[derive(Debug, Copy, Clone, PartialEq, Eq)] +6775enum PathBlocked { +6776 No, +6777 AntiAmplification, +6778 Congestion, +6779 Pacing, +6780} +6781 +6782/// Fields of `Connection` specific to it being client-side or server-side +6783enum ConnectionSide { +6784 Client { +6785 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded +6786 token: Bytes, +6787 token_store: Arc<dyn TokenStore>, +6788 server_name: String, +6789 }, +6790 Server { +6791 server_config: Arc<ServerConfig>, +6792 }, +6793} +6794 +6795impl ConnectionSide { +6796 fn remote_may_migrate(&self, state: &State) -> bool { +6797 match self { +6798 Self::Server { server_config } => server_config.migration, +6799 Self::Client { .. } => { +6800 if let Some(hs) = state.as_handshake() { +6801 hs.allow_server_migration +6802 } else { +6803 false +6804 } +6805 } +6806 } +6807 } +6808 +6809 fn is_client(&self) -> bool { +6810 self.side().is_client() +6811 } +6812 +6813 fn is_server(&self) -> bool { +6814 self.side().is_server() +6815 } +6816 +6817 fn side(&self) -> Side { +6818 match *self { +6819 Self::Client { .. } => Side::Client, +6820 Self::Server { .. } => Side::Server, +6821 } +6822 } +6823} +6824 +6825impl From<SideArgs> for ConnectionSide { +6826 fn from(side: SideArgs) -> Self { +6827 match side { +6828 SideArgs::Client { +6829 token_store, +6830 server_name, +6831 } => Self::Client { +6832 token: token_store.take(&server_name).unwrap_or_default(), +6833 token_store, +6834 server_name, +6835 }, +6836 SideArgs::Server { +6837 server_config, +6838 pref_addr_cid: _, +6839 path_validated: _, +6840 } => Self::Server { server_config }, +6841 } +6842 } +6843} +6844 +6845/// Parameters to `Connection::new` specific to it being client-side or server-side +6846pub(crate) enum SideArgs { +6847 Client { +6848 token_store: Arc<dyn TokenStore>, +6849 server_name: String, +6850 }, +6851 Server { +6852 server_config: Arc<ServerConfig>, +6853 pref_addr_cid: Option<ConnectionId>, +6854 path_validated: bool, +6855 }, +6856} 6857 -6858 fn is_server(&self) -> bool { -6859 self.side().is_server() -6860 } -6861 -6862 fn side(&self) -> Side { -6863 match *self { -6864 Self::Client { .. } => Side::Client, -6865 Self::Server { .. } => Side::Server, -6866 } -6867 } -6868} -6869 -6870impl From<SideArgs> for ConnectionSide { -6871 fn from(side: SideArgs) -> Self { -6872 match side { -6873 SideArgs::Client { -6874 token_store, -6875 server_name, -6876 } => Self::Client { -6877 token: token_store.take(&server_name).unwrap_or_default(), -6878 token_store, -6879 server_name, -6880 }, -6881 SideArgs::Server { -6882 server_config, -6883 pref_addr_cid: _, -6884 path_validated: _, -6885 } => Self::Server { server_config }, -6886 } -6887 } -6888} -6889 -6890/// Parameters to `Connection::new` specific to it being client-side or server-side -6891pub(crate) enum SideArgs { -6892 Client { -6893 token_store: Arc<dyn TokenStore>, -6894 server_name: String, -6895 }, -6896 Server { -6897 server_config: Arc<ServerConfig>, -6898 pref_addr_cid: Option<ConnectionId>, -6899 path_validated: bool, -6900 }, -6901} -6902 -6903impl SideArgs { -6904 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { -6905 match *self { -6906 Self::Client { .. } => None, -6907 Self::Server { pref_addr_cid, .. } => pref_addr_cid, -6908 } -6909 } -6910 -6911 pub(crate) fn path_validated(&self) -> bool { -6912 match *self { -6913 Self::Client { .. } => true, -6914 Self::Server { path_validated, .. } => path_validated, -6915 } -6916 } -6917 -6918 pub(crate) fn side(&self) -> Side { -6919 match *self { -6920 Self::Client { .. } => Side::Client, -6921 Self::Server { .. } => Side::Server, -6922 } -6923 } -6924} -6925 -6926/// Reasons why a connection might be lost -6927#[derive(Debug, Error, Clone, PartialEq, Eq)] -6928pub enum ConnectionError { -6929 /// The peer doesn't implement any supported version -6930 #[error("peer doesn't implement any supported version")] -6931 VersionMismatch, -6932 /// The peer violated the QUIC specification as understood by this implementation -6933 #[error(transparent)] -6934 TransportError(#[from] TransportError), -6935 /// The peer's QUIC stack aborted the connection automatically -6936 #[error("aborted by peer: {0}")] -6937 ConnectionClosed(frame::ConnectionClose), -6938 /// The peer closed the connection -6939 #[error("closed by peer: {0}")] -6940 ApplicationClosed(frame::ApplicationClose), -6941 /// The peer is unable to continue processing this connection, usually due to having restarted -6942 #[error("reset by peer")] -6943 Reset, -6944 /// Communication with the peer has lapsed for longer than the negotiated idle timeout -6945 /// -6946 /// If neither side is sending keep-alives, a connection will time out after a long enough idle -6947 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] -6948 /// and [`TransportConfig::keep_alive_interval()`]. -6949 #[error("timed out")] -6950 TimedOut, -6951 /// The local application closed the connection -6952 #[error("closed")] -6953 LocallyClosed, -6954 /// The connection could not be created because not enough of the CID space is available -6955 /// -6956 /// Try using longer connection IDs. -6957 #[error("CIDs exhausted")] -6958 CidsExhausted, -6959} -6960 -6961impl From<Close> for ConnectionError { -6962 fn from(x: Close) -> Self { -6963 match x { -6964 Close::Connection(reason) => Self::ConnectionClosed(reason), -6965 Close::Application(reason) => Self::ApplicationClosed(reason), -6966 } -6967 } -6968} -6969 -6970// For compatibility with API consumers -6971impl From<ConnectionError> for io::Error { -6972 fn from(x: ConnectionError) -> Self { -6973 use ConnectionError::*; -6974 let kind = match x { -6975 TimedOut => io::ErrorKind::TimedOut, -6976 Reset => io::ErrorKind::ConnectionReset, -6977 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, -6978 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { -6979 io::ErrorKind::Other -6980 } -6981 }; -6982 Self::new(kind, x) -6983 } +6858impl SideArgs { +6859 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { +6860 match *self { +6861 Self::Client { .. } => None, +6862 Self::Server { pref_addr_cid, .. } => pref_addr_cid, +6863 } +6864 } +6865 +6866 pub(crate) fn path_validated(&self) -> bool { +6867 match *self { +6868 Self::Client { .. } => true, +6869 Self::Server { path_validated, .. } => path_validated, +6870 } +6871 } +6872 +6873 pub(crate) fn side(&self) -> Side { +6874 match *self { +6875 Self::Client { .. } => Side::Client, +6876 Self::Server { .. } => Side::Server, +6877 } +6878 } +6879} +6880 +6881/// Reasons why a connection might be lost +6882#[derive(Debug, Error, Clone, PartialEq, Eq)] +6883pub enum ConnectionError { +6884 /// The peer doesn't implement any supported version +6885 #[error("peer doesn't implement any supported version")] +6886 VersionMismatch, +6887 /// The peer violated the QUIC specification as understood by this implementation +6888 #[error(transparent)] +6889 TransportError(#[from] TransportError), +6890 /// The peer's QUIC stack aborted the connection automatically +6891 #[error("aborted by peer: {0}")] +6892 ConnectionClosed(frame::ConnectionClose), +6893 /// The peer closed the connection +6894 #[error("closed by peer: {0}")] +6895 ApplicationClosed(frame::ApplicationClose), +6896 /// The peer is unable to continue processing this connection, usually due to having restarted +6897 #[error("reset by peer")] +6898 Reset, +6899 /// Communication with the peer has lapsed for longer than the negotiated idle timeout +6900 /// +6901 /// If neither side is sending keep-alives, a connection will time out after a long enough idle +6902 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] +6903 /// and [`TransportConfig::keep_alive_interval()`]. +6904 #[error("timed out")] +6905 TimedOut, +6906 /// The local application closed the connection +6907 #[error("closed")] +6908 LocallyClosed, +6909 /// The connection could not be created because not enough of the CID space is available +6910 /// +6911 /// Try using longer connection IDs. +6912 #[error("CIDs exhausted")] +6913 CidsExhausted, +6914} +6915 +6916impl From<Close> for ConnectionError { +6917 fn from(x: Close) -> Self { +6918 match x { +6919 Close::Connection(reason) => Self::ConnectionClosed(reason), +6920 Close::Application(reason) => Self::ApplicationClosed(reason), +6921 } +6922 } +6923} +6924 +6925// For compatibility with API consumers +6926impl From<ConnectionError> for io::Error { +6927 fn from(x: ConnectionError) -> Self { +6928 use ConnectionError::*; +6929 let kind = match x { +6930 TimedOut => io::ErrorKind::TimedOut, +6931 Reset => io::ErrorKind::ConnectionReset, +6932 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, +6933 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { +6934 io::ErrorKind::Other +6935 } +6936 }; +6937 Self::new(kind, x) +6938 } +6939} +6940 +6941/// Errors that might trigger a path being closed +6942// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API +6943#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] +6944pub enum PathError { +6945 /// The extension was not negotiated with the peer +6946 #[error("multipath extension not negotiated")] +6947 MultipathNotNegotiated, +6948 /// Paths can only be opened client-side +6949 #[error("the server side may not open a path")] +6950 ServerSideNotAllowed, +6951 /// Current limits do not allow us to open more paths +6952 #[error("maximum number of concurrent paths reached")] +6953 MaxPathIdReached, +6954 /// No remote CIDs available to open a new path +6955 #[error("remoted CIDs exhausted")] +6956 RemoteCidsExhausted, +6957 /// Path could not be validated and will be abandoned +6958 #[error("path validation failed")] +6959 ValidationFailed, +6960 /// The remote address for the path is not supported by the endpoint +6961 #[error("invalid remote address")] +6962 InvalidRemoteAddress(SocketAddr), +6963} +6964 +6965/// Errors triggered when abandoning a path +6966#[derive(Debug, Error, Clone, Eq, PartialEq)] +6967pub enum ClosePathError { +6968 /// Multipath is not negotiated +6969 #[error("Multipath extension not negotiated")] +6970 MultipathNotNegotiated, +6971 /// The path is already closed or was never opened +6972 #[error("closed path")] +6973 ClosedPath, +6974 /// This is the last path, which can not be abandoned +6975 #[error("last open path")] +6976 LastOpenPath, +6977} +6978 +6979/// Error when the multipath extension was not negotiated, but attempted to be used. +6980#[derive(Debug, Error, Clone, Copy)] +6981#[error("Multipath extension not negotiated")] +6982pub struct MultipathNotNegotiated { +6983 _private: (), 6984} 6985 -6986/// Errors that might trigger a path being closed -6987// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API -6988#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] -6989pub enum PathError { -6990 /// The extension was not negotiated with the peer -6991 #[error("multipath extension not negotiated")] -6992 MultipathNotNegotiated, -6993 /// Paths can only be opened client-side -6994 #[error("the server side may not open a path")] -6995 ServerSideNotAllowed, -6996 /// Current limits do not allow us to open more paths -6997 #[error("maximum number of concurrent paths reached")] -6998 MaxPathIdReached, -6999 /// No remote CIDs available to open a new path -7000 #[error("remoted CIDs exhausted")] -7001 RemoteCidsExhausted, -7002 /// Path could not be validated and will be abandoned -7003 #[error("path validation failed")] -7004 ValidationFailed, -7005 /// The remote address for the path is not supported by the endpoint -7006 #[error("invalid remote address")] -7007 InvalidRemoteAddress(SocketAddr), -7008} -7009 -7010/// Errors triggered when abandoning a path -7011#[derive(Debug, Error, Clone, Eq, PartialEq)] -7012pub enum ClosePathError { -7013 /// Multipath is not negotiated -7014 #[error("Multipath extension not negotiated")] -7015 MultipathNotNegotiated, -7016 /// The path is already closed or was never opened -7017 #[error("closed path")] -7018 ClosedPath, -7019 /// This is the last path, which can not be abandoned -7020 #[error("last open path")] -7021 LastOpenPath, +6986/// Events of interest to the application +6987#[derive(Debug)] +6988pub enum Event { +6989 /// The connection's handshake data is ready +6990 HandshakeDataReady, +6991 /// The connection was successfully established +6992 Connected, +6993 /// The TLS handshake was confirmed +6994 HandshakeConfirmed, +6995 /// The connection was lost +6996 /// +6997 /// Emitted if the peer closes the connection or an error is encountered. +6998 ConnectionLost { +6999 /// Reason that the connection was closed +7000 reason: ConnectionError, +7001 }, +7002 /// Stream events +7003 Stream(StreamEvent), +7004 /// One or more application datagrams have been received +7005 DatagramReceived, +7006 /// One or more application datagrams have been sent after blocking +7007 DatagramsUnblocked, +7008 /// (Multi)Path events +7009 Path(PathEvent), +7010 /// Iroh's nat traversal events +7011 NatTraversal(iroh_hp::Event), +7012} +7013 +7014impl From<PathEvent> for Event { +7015 fn from(source: PathEvent) -> Self { +7016 Self::Path(source) +7017 } +7018} +7019 +7020fn get_max_ack_delay(params: &TransportParameters) -> Duration { +7021 Duration::from_micros(params.max_ack_delay.0 * 1000) 7022} 7023 -7024/// Error when the multipath extension was not negotiated, but attempted to be used. -7025#[derive(Debug, Error, Clone, Copy)] -7026#[error("Multipath extension not negotiated")] -7027pub struct MultipathNotNegotiated { -7028 _private: (), -7029} -7030 -7031/// Events of interest to the application -7032#[derive(Debug)] -7033pub enum Event { -7034 /// The connection's handshake data is ready -7035 HandshakeDataReady, -7036 /// The connection was successfully established -7037 Connected, -7038 /// The TLS handshake was confirmed -7039 HandshakeConfirmed, -7040 /// The connection was lost -7041 /// -7042 /// Emitted if the peer closes the connection or an error is encountered. -7043 ConnectionLost { -7044 /// Reason that the connection was closed -7045 reason: ConnectionError, -7046 }, -7047 /// Stream events -7048 Stream(StreamEvent), -7049 /// One or more application datagrams have been received -7050 DatagramReceived, -7051 /// One or more application datagrams have been sent after blocking -7052 DatagramsUnblocked, -7053 /// (Multi)Path events -7054 Path(PathEvent), -7055 /// Iroh's nat traversal events -7056 NatTraversal(iroh_hp::Event), -7057} -7058 -7059impl From<PathEvent> for Event { -7060 fn from(source: PathEvent) -> Self { -7061 Self::Path(source) -7062 } -7063} +7024// Prevents overflow and improves behavior in extreme circumstances +7025const MAX_BACKOFF_EXPONENT: u32 = 16; +7026 +7027/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag +7028/// +7029/// This must be at least as large as the header for a well-formed empty packet to be coalesced, +7030/// plus some space for frames. We only care about handshake headers because short header packets +7031/// necessarily have smaller headers, and initial packets are only ever the first packet in a +7032/// datagram (because we coalesce in ascending packet space order and the only reason to split a +7033/// packet is when packet space changes). +7034const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; +7035 +7036/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header +7037/// +7038/// Excludes packet-type-specific fields such as packet number or Initial token +7039// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + +7040// scid len + scid + length + pn +7041const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = +7042 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; +7043 +7044#[derive(Default)] +7045struct SentFrames { +7046 retransmits: ThinRetransmits, +7047 /// The packet number of the largest acknowledged packet for each path +7048 largest_acked: FxHashMap<PathId, u64>, +7049 stream_frames: StreamMetaVec, +7050 /// Whether the packet contains non-retransmittable frames (like datagrams) +7051 non_retransmits: bool, +7052 /// If the datagram containing these frames should be padded to the min MTU +7053 requires_padding: bool, +7054} +7055 +7056impl SentFrames { +7057 /// Returns whether the packet contains only ACKs +7058 fn is_ack_only(&self, streams: &StreamsState) -> bool { +7059 !self.largest_acked.is_empty() +7060 && !self.non_retransmits +7061 && self.stream_frames.is_empty() +7062 && self.retransmits.is_empty(streams) +7063 } 7064 -7065fn get_max_ack_delay(params: &TransportParameters) -> Duration { -7066 Duration::from_micros(params.max_ack_delay.0 * 1000) -7067} +7065 fn retransmits_mut(&mut self) -> &mut Retransmits { +7066 self.retransmits.get_or_create() +7067 } 7068 -7069// Prevents overflow and improves behavior in extreme circumstances -7070const MAX_BACKOFF_EXPONENT: u32 = 16; -7071 -7072/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag -7073/// -7074/// This must be at least as large as the header for a well-formed empty packet to be coalesced, -7075/// plus some space for frames. We only care about handshake headers because short header packets -7076/// necessarily have smaller headers, and initial packets are only ever the first packet in a -7077/// datagram (because we coalesce in ascending packet space order and the only reason to split a -7078/// packet is when packet space changes). -7079const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; -7080 -7081/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header -7082/// -7083/// Excludes packet-type-specific fields such as packet number or Initial token -7084// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + -7085// scid len + scid + length + pn -7086const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = -7087 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; -7088 -7089#[derive(Default)] -7090struct SentFrames { -7091 retransmits: ThinRetransmits, -7092 /// The packet number of the largest acknowledged packet for each path -7093 largest_acked: FxHashMap<PathId, u64>, -7094 stream_frames: StreamMetaVec, -7095 /// Whether the packet contains non-retransmittable frames (like datagrams) -7096 non_retransmits: bool, -7097 /// If the datagram containing these frames should be padded to the min MTU -7098 requires_padding: bool, -7099} -7100 -7101impl SentFrames { -7102 /// Returns whether the packet contains only ACKs -7103 fn is_ack_only(&self, streams: &StreamsState) -> bool { -7104 !self.largest_acked.is_empty() -7105 && !self.non_retransmits -7106 && self.stream_frames.is_empty() -7107 && self.retransmits.is_empty(streams) -7108 } -7109 -7110 fn retransmits_mut(&mut self) -> &mut Retransmits { -7111 self.retransmits.get_or_create() -7112 } -7113 -7114 fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) { -7115 use frame::EncodableFrame::*; -7116 match frame { -7117 PathAck(path_ack_encoder) => { -7118 if let Some(max) = path_ack_encoder.ranges.max() { -7119 self.largest_acked.insert(path_ack_encoder.path_id, max); -7120 } -7121 } -7122 Ack(ack_encoder) => { -7123 if let Some(max) = ack_encoder.ranges.max() { -7124 self.largest_acked.insert(PathId::ZERO, max); -7125 } +7069 fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) { +7070 use frame::EncodableFrame::*; +7071 match frame { +7072 PathAck(path_ack_encoder) => { +7073 if let Some(max) = path_ack_encoder.ranges.max() { +7074 self.largest_acked.insert(path_ack_encoder.path_id, max); +7075 } +7076 } +7077 Ack(ack_encoder) => { +7078 if let Some(max) = ack_encoder.ranges.max() { +7079 self.largest_acked.insert(PathId::ZERO, max); +7080 } +7081 } +7082 Close(_) => { /* non retransmittable, but after this we don't really care */ } +7083 PathResponse(_) => self.non_retransmits = true, +7084 HandshakeDone(_) => self.retransmits_mut().handshake_done = true, +7085 ReachOut(frame::ReachOut { round, ip, port }) => { +7086 let (recorded_round, reach_outs) = self +7087 .retransmits_mut() +7088 .reach_out +7089 .get_or_insert_with(|| (round, FxHashSet::default())); +7090 // Only record reach outs for the current round or a newer than the recorded one. +7091 if *recorded_round == round { +7092 // Same round, simply append. +7093 reach_outs.insert((ip, port)); +7094 } else if *recorded_round < round { +7095 // New round. +7096 *recorded_round = round; +7097 reach_outs.drain(); +7098 reach_outs.insert((ip, port)); +7099 } else { +7100 // ignore old reach out that was sent +7101 } +7102 } +7103 +7104 ObservedAddr(_) => self.retransmits_mut().observed_addr = true, +7105 Ping(_) => self.non_retransmits = true, +7106 ImmediateAck(_) => self.non_retransmits = true, +7107 AckFrequency(_) => self.retransmits_mut().ack_frequency = true, +7108 PathChallenge(_) => self.non_retransmits = true, +7109 Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto), +7110 PathAbandon(path_abandon) => { +7111 self.retransmits_mut() +7112 .path_abandon +7113 .entry(path_abandon.path_id) +7114 .or_insert(path_abandon.error_code); +7115 } +7116 PathStatusAvailable(frame::PathStatusAvailable { path_id, .. }) +7117 | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => { +7118 self.retransmits_mut().path_status.insert(path_id); +7119 } +7120 MaxPathId(_) => self.retransmits_mut().max_path_id = true, +7121 PathsBlocked(_) => self.retransmits_mut().paths_blocked = true, +7122 PathCidsBlocked(path_cids_blocked) => { +7123 self.retransmits_mut() +7124 .path_cids_blocked +7125 .insert(path_cids_blocked.path_id); 7126 } -7127 Close(_) => { /* non retransmittable, but after this we don't really care */ } -7128 PathResponse(_) => self.non_retransmits = true, -7129 HandshakeDone(_) => self.retransmits_mut().handshake_done = true, -7130 ReachOut(frame::ReachOut { round, ip, port }) => { -7131 let (recorded_round, reach_outs) = self -7132 .retransmits_mut() -7133 .reach_out -7134 .get_or_insert_with(|| (round, FxHashSet::default())); -7135 // Only record reach outs for the current round or a newer than the recorded one. -7136 if *recorded_round == round { -7137 // Same round, simply append. -7138 reach_outs.insert((ip, port)); -7139 } else if *recorded_round < round { -7140 // New round. -7141 *recorded_round = round; -7142 reach_outs.drain(); -7143 reach_outs.insert((ip, port)); -7144 } else { -7145 // ignore old reach out that was sent -7146 } -7147 } -7148 -7149 ObservedAddr(_) => self.retransmits_mut().observed_addr = true, -7150 Ping(_) => self.non_retransmits = true, -7151 ImmediateAck(_) => self.non_retransmits = true, -7152 AckFrequency(_) => self.retransmits_mut().ack_frequency = true, -7153 PathChallenge(_) => self.non_retransmits = true, -7154 Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto), -7155 PathAbandon(path_abandon) => { -7156 self.retransmits_mut() -7157 .path_abandon -7158 .entry(path_abandon.path_id) -7159 .or_insert(path_abandon.error_code); -7160 } -7161 PathStatusAvailable(frame::PathStatusAvailable { path_id, .. }) -7162 | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => { -7163 self.retransmits_mut().path_status.insert(path_id); -7164 } -7165 MaxPathId(_) => self.retransmits_mut().max_path_id = true, -7166 PathsBlocked(_) => self.retransmits_mut().paths_blocked = true, -7167 PathCidsBlocked(path_cids_blocked) => { -7168 self.retransmits_mut() -7169 .path_cids_blocked -7170 .insert(path_cids_blocked.path_id); -7171 } -7172 ResetStream(reset) => self -7173 .retransmits_mut() -7174 .reset_stream -7175 .push((reset.id, reset.error_code)), -7176 StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending), -7177 NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()), -7178 RetireConnectionId(retire_cid) => self -7179 .retransmits_mut() -7180 .retire_cids -7181 .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)), -7182 Datagram(_) => self.non_retransmits = true, -7183 NewToken(_) => {} -7184 AddAddress(add_address) => { -7185 self.retransmits_mut().add_address.insert(add_address); -7186 } -7187 RemoveAddress(remove_address) => { -7188 self.retransmits_mut().remove_address.insert(remove_address); -7189 } -7190 StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta), -7191 MaxData(_) => self.retransmits_mut().max_data = true, -7192 MaxStreamData(max) => { -7193 self.retransmits_mut().max_stream_data.insert(max.id); -7194 } -7195 MaxStreams(max_streams) => { -7196 self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true -7197 } -7198 } -7199 } -7200} -7201 -7202/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. -7203/// -7204/// According to the definition of max_idle_timeout, a value of `0` means the timeout is disabled; see <https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.4.1.> -7205/// -7206/// According to the negotiation procedure, either the minimum of the timeouts or one specified is used as the negotiated value; see <https://www.rfc-editor.org/rfc/rfc9000#section-10.1-2.> -7207/// -7208/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. -7209fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { -7210 match (x, y) { -7211 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, -7212 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), -7213 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), -7214 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), -7215 } -7216} -7217 -7218#[cfg(test)] -7219mod tests { -7220 use super::*; -7221 -7222 #[test] -7223 fn negotiate_max_idle_timeout_commutative() { -7224 let test_params = [ -7225 (None, None, None), -7226 (None, Some(VarInt(0)), None), -7227 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), -7228 (Some(VarInt(0)), Some(VarInt(0)), None), -7229 ( -7230 Some(VarInt(2)), -7231 Some(VarInt(0)), -7232 Some(Duration::from_millis(2)), -7233 ), -7234 ( -7235 Some(VarInt(1)), -7236 Some(VarInt(4)), -7237 Some(Duration::from_millis(1)), -7238 ), -7239 ]; -7240 -7241 for (left, right, result) in test_params { -7242 assert_eq!(negotiate_max_idle_timeout(left, right), result); -7243 assert_eq!(negotiate_max_idle_timeout(right, left), result); -7244 } -7245 } -7246}

\ No newline at end of file +7127 ResetStream(reset) => self +7128 .retransmits_mut() +7129 .reset_stream +7130 .push((reset.id, reset.error_code)), +7131 StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending), +7132 NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()), +7133 RetireConnectionId(retire_cid) => self +7134 .retransmits_mut() +7135 .retire_cids +7136 .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)), +7137 Datagram(_) => self.non_retransmits = true, +7138 NewToken(_) => {} +7139 AddAddress(add_address) => { +7140 self.retransmits_mut().add_address.insert(add_address); +7141 } +7142 RemoveAddress(remove_address) => { +7143 self.retransmits_mut().remove_address.insert(remove_address); +7144 } +7145 StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta), +7146 MaxData(_) => self.retransmits_mut().max_data = true, +7147 MaxStreamData(max) => { +7148 self.retransmits_mut().max_stream_data.insert(max.id); +7149 } +7150 MaxStreams(max_streams) => { +7151 self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true +7152 } +7153 } +7154 } +7155} +7156 +7157/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. +7158/// +7159/// According to the definition of max_idle_timeout, a value of `0` means the timeout is disabled; see <https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.4.1.> +7160/// +7161/// According to the negotiation procedure, either the minimum of the timeouts or one specified is used as the negotiated value; see <https://www.rfc-editor.org/rfc/rfc9000#section-10.1-2.> +7162/// +7163/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. +7164fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { +7165 match (x, y) { +7166 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, +7167 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), +7168 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), +7169 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), +7170 } +7171} +7172 +7173#[cfg(test)] +7174mod tests { +7175 use super::*; +7176 +7177 #[test] +7178 fn negotiate_max_idle_timeout_commutative() { +7179 let test_params = [ +7180 (None, None, None), +7181 (None, Some(VarInt(0)), None), +7182 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), +7183 (Some(VarInt(0)), Some(VarInt(0)), None), +7184 ( +7185 Some(VarInt(2)), +7186 Some(VarInt(0)), +7187 Some(Duration::from_millis(2)), +7188 ), +7189 ( +7190 Some(VarInt(1)), +7191 Some(VarInt(4)), +7192 Some(Duration::from_millis(1)), +7193 ), +7194 ]; +7195 +7196 for (left, right, result) in test_params { +7197 assert_eq!(negotiate_max_idle_timeout(left, right), result); +7198 assert_eq!(negotiate_max_idle_timeout(right, left), result); +7199 } +7200 } +7201}

\ No newline at end of file diff --git a/pr/436/docs/src/iroh_quinn_proto/connection/paths.rs.html b/pr/436/docs/src/iroh_quinn_proto/connection/paths.rs.html index 37c9a09b9..40f1d07ca 100644 --- a/pr/436/docs/src/iroh_quinn_proto/connection/paths.rs.html +++ b/pr/436/docs/src/iroh_quinn_proto/connection/paths.rs.html @@ -212,762 +212,757 @@ 212 /// irreversible, since it's not affected by the path being closed. 213 pub(super) open: bool, 214 -215 /// State relevant to sending and receiving PATH_ABANDON frames. -216 pub(super) abandon_state: AbandonState, -217 -218 /// Snapshot of the qlog recovery metrics -219 #[cfg(feature = "qlog")] -220 recovery_metrics: RecoveryMetrics, -221 -222 /// Tag uniquely identifying a path in a connection -223 generation: u64, -224} -225 -226/// The abandon-relevant state a path can be in. -227#[derive(Debug)] -228pub(super) enum AbandonState { -229 /// This path wasn't abandoned yet. -230 NotAbandoned, -231 /// We either received or sent a PATH_ABANDON and are only draining this path -232 /// until we discard it. -233 Draining, -234} -235 -236impl PathData { -237 pub(super) fn new( -238 network_path: FourTuple, -239 allow_mtud: bool, -240 peer_max_udp_payload_size: Option<u16>, -241 generation: u64, -242 now: Instant, -243 config: &TransportConfig, -244 ) -> Self { -245 let congestion = config -246 .congestion_controller_factory -247 .clone() -248 .build(now, config.get_initial_mtu()); -249 Self { -250 network_path, -251 rtt: RttEstimator::new(config.initial_rtt), -252 sending_ecn: true, -253 pacing: Pacer::new( -254 config.initial_rtt, -255 congestion.initial_window(), -256 config.get_initial_mtu(), -257 now, -258 ), -259 congestion, -260 challenges_sent: Default::default(), -261 send_new_challenge: false, -262 path_responses: PathResponses::default(), -263 validated: false, -264 total_sent: 0, -265 total_recvd: 0, -266 mtud: config -267 .mtu_discovery_config -268 .as_ref() -269 .filter(|_| allow_mtud) -270 .map_or_else( -271 || MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu), -272 |mtud_config| { -273 MtuDiscovery::new( -274 config.get_initial_mtu(), -275 config.min_mtu, -276 peer_max_udp_payload_size, -277 mtud_config.clone(), -278 ) -279 }, -280 ), -281 first_packet_after_rtt_sample: None, -282 in_flight: InFlight::new(), -283 observed_addr_sent: false, -284 last_observed_addr_report: None, -285 status: Default::default(), -286 first_packet: None, -287 pto_count: 0, -288 idle_timeout: config.default_path_max_idle_timeout, -289 keep_alive: config.default_path_keep_alive_interval, -290 open: false, -291 abandon_state: AbandonState::NotAbandoned, -292 #[cfg(feature = "qlog")] -293 recovery_metrics: RecoveryMetrics::default(), -294 generation, -295 } -296 } -297 -298 /// Create a new path from a previous one. -299 /// -300 /// This should only be called when migrating paths. -301 pub(super) fn from_previous( -302 network_path: FourTuple, -303 prev: &Self, -304 generation: u64, -305 now: Instant, -306 ) -> Self { -307 let congestion = prev.congestion.clone_box(); -308 let smoothed_rtt = prev.rtt.get(); -309 Self { -310 network_path, -311 rtt: prev.rtt, -312 pacing: Pacer::new(smoothed_rtt, congestion.window(), prev.current_mtu(), now), -313 sending_ecn: true, -314 congestion, -315 challenges_sent: Default::default(), -316 send_new_challenge: false, -317 path_responses: PathResponses::default(), -318 validated: false, -319 total_sent: 0, -320 total_recvd: 0, -321 mtud: prev.mtud.clone(), -322 first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample, -323 in_flight: InFlight::new(), -324 observed_addr_sent: false, -325 last_observed_addr_report: None, -326 status: prev.status.clone(), -327 first_packet: None, -328 pto_count: 0, -329 idle_timeout: prev.idle_timeout, -330 keep_alive: prev.keep_alive, -331 open: false, -332 abandon_state: AbandonState::NotAbandoned, -333 #[cfg(feature = "qlog")] -334 recovery_metrics: prev.recovery_metrics.clone(), -335 generation, -336 } +215 /// Whether we're currently draining the path after having abandoned it. +216 /// +217 /// This should only be true when a path discard timer is armed, and after the path was +218 /// abandoned (and added to the abandoned_paths set). +219 /// +220 /// This will only ever be set from false to true. +221 pub(super) draining: bool, +222 +223 /// Snapshot of the qlog recovery metrics +224 #[cfg(feature = "qlog")] +225 recovery_metrics: RecoveryMetrics, +226 +227 /// Tag uniquely identifying a path in a connection +228 generation: u64, +229} +230 +231impl PathData { +232 pub(super) fn new( +233 network_path: FourTuple, +234 allow_mtud: bool, +235 peer_max_udp_payload_size: Option<u16>, +236 generation: u64, +237 now: Instant, +238 config: &TransportConfig, +239 ) -> Self { +240 let congestion = config +241 .congestion_controller_factory +242 .clone() +243 .build(now, config.get_initial_mtu()); +244 Self { +245 network_path, +246 rtt: RttEstimator::new(config.initial_rtt), +247 sending_ecn: true, +248 pacing: Pacer::new( +249 config.initial_rtt, +250 congestion.initial_window(), +251 config.get_initial_mtu(), +252 now, +253 ), +254 congestion, +255 challenges_sent: Default::default(), +256 send_new_challenge: false, +257 path_responses: PathResponses::default(), +258 validated: false, +259 total_sent: 0, +260 total_recvd: 0, +261 mtud: config +262 .mtu_discovery_config +263 .as_ref() +264 .filter(|_| allow_mtud) +265 .map_or_else( +266 || MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu), +267 |mtud_config| { +268 MtuDiscovery::new( +269 config.get_initial_mtu(), +270 config.min_mtu, +271 peer_max_udp_payload_size, +272 mtud_config.clone(), +273 ) +274 }, +275 ), +276 first_packet_after_rtt_sample: None, +277 in_flight: InFlight::new(), +278 observed_addr_sent: false, +279 last_observed_addr_report: None, +280 status: Default::default(), +281 first_packet: None, +282 pto_count: 0, +283 idle_timeout: config.default_path_max_idle_timeout, +284 keep_alive: config.default_path_keep_alive_interval, +285 open: false, +286 draining: false, +287 #[cfg(feature = "qlog")] +288 recovery_metrics: RecoveryMetrics::default(), +289 generation, +290 } +291 } +292 +293 /// Create a new path from a previous one. +294 /// +295 /// This should only be called when migrating paths. +296 pub(super) fn from_previous( +297 network_path: FourTuple, +298 prev: &Self, +299 generation: u64, +300 now: Instant, +301 ) -> Self { +302 let congestion = prev.congestion.clone_box(); +303 let smoothed_rtt = prev.rtt.get(); +304 Self { +305 network_path, +306 rtt: prev.rtt, +307 pacing: Pacer::new(smoothed_rtt, congestion.window(), prev.current_mtu(), now), +308 sending_ecn: true, +309 congestion, +310 challenges_sent: Default::default(), +311 send_new_challenge: false, +312 path_responses: PathResponses::default(), +313 validated: false, +314 total_sent: 0, +315 total_recvd: 0, +316 mtud: prev.mtud.clone(), +317 first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample, +318 in_flight: InFlight::new(), +319 observed_addr_sent: false, +320 last_observed_addr_report: None, +321 status: prev.status.clone(), +322 first_packet: None, +323 pto_count: 0, +324 idle_timeout: prev.idle_timeout, +325 keep_alive: prev.keep_alive, +326 open: false, +327 draining: false, +328 #[cfg(feature = "qlog")] +329 recovery_metrics: prev.recovery_metrics.clone(), +330 generation, +331 } +332 } +333 +334 /// Whether we're in the process of validating this path with PATH_CHALLENGEs +335 pub(super) fn is_validating_path(&self) -> bool { +336 !self.challenges_sent.is_empty() || self.send_new_challenge 337 } 338 -339 /// Whether we're in the process of validating this path with PATH_CHALLENGEs -340 pub(super) fn is_validating_path(&self) -> bool { -341 !self.challenges_sent.is_empty() || self.send_new_challenge -342 } -343 -344 /// Indicates whether we're a server that hasn't validated the peer's address and hasn't -345 /// received enough data from the peer to permit sending `bytes_to_send` additional bytes -346 pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool { -347 !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send +339 /// Indicates whether we're a server that hasn't validated the peer's address and hasn't +340 /// received enough data from the peer to permit sending `bytes_to_send` additional bytes +341 pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool { +342 !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send +343 } +344 +345 /// Returns the path's current MTU +346 pub(super) fn current_mtu(&self) -> u16 { +347 self.mtud.current_mtu() 348 } 349 -350 /// Returns the path's current MTU -351 pub(super) fn current_mtu(&self) -> u16 { -352 self.mtud.current_mtu() -353 } -354 -355 /// Account for transmission of `packet` with number `pn` in `space` -356 pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) { -357 self.in_flight.insert(&packet); -358 if self.first_packet.is_none() { -359 self.first_packet = Some(pn); -360 } -361 if let Some(forgotten) = space.sent(pn, packet) { -362 self.remove_in_flight(&forgotten); -363 } -364 } -365 -366 /// Remove `packet` with number `pn` from this path's congestion control counters, or return -367 /// `false` if `pn` was sent before this path was established. -368 pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool { -369 if packet.path_generation != self.generation { -370 return false; -371 } -372 self.in_flight.remove(packet); -373 true -374 } -375 -376 /// Increment the total size of sent UDP datagrams -377 pub(super) fn inc_total_sent(&mut self, inc: u64) { -378 self.total_sent = self.total_sent.saturating_add(inc); -379 if !self.validated { -380 trace!( -381 network_path = %self.network_path, -382 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), -383 "anti amplification budget decreased" -384 ); -385 } -386 } -387 -388 /// Increment the total size of received UDP datagrams -389 pub(super) fn inc_total_recvd(&mut self, inc: u64) { -390 self.total_recvd = self.total_recvd.saturating_add(inc); -391 if !self.validated { -392 trace!( -393 network_path = %self.network_path, -394 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), -395 "anti amplification budget increased" -396 ); -397 } -398 } -399 -400 /// The earliest time at which a sent challenge is considered lost. -401 pub(super) fn earliest_expiring_challenge(&self) -> Option<Instant> { -402 if self.challenges_sent.is_empty() { -403 return None; -404 } -405 let pto = self.rtt.pto_base(); -406 self.challenges_sent -407 .values() -408 .map(|info| info.sent_instant) -409 .min() -410 .map(|sent_instant| sent_instant + pto) -411 } -412 -413 /// Handle receiving a PATH_RESPONSE. -414 pub(super) fn on_path_response_received( -415 &mut self, -416 now: Instant, -417 token: u64, -418 network_path: FourTuple, -419 ) -> OnPathResponseReceived { -420 match self.challenges_sent.get(&token) { -421 // Response to an on-path PathChallenge -422 Some(info) -423 if info.network_path.is_probably_same_path(&network_path) -424 && self.network_path.is_probably_same_path(&network_path) => -425 { -426 self.network_path.update_local_if_same_remote(&network_path); -427 let sent_instant = info.sent_instant; -428 if !std::mem::replace(&mut self.validated, true) { -429 trace!("new path validated"); -430 } -431 // Clear any other on-path sent challenge. -432 self.challenges_sent -433 .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path)); -434 -435 self.send_new_challenge = false; +350 /// Account for transmission of `packet` with number `pn` in `space` +351 pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) { +352 self.in_flight.insert(&packet); +353 if self.first_packet.is_none() { +354 self.first_packet = Some(pn); +355 } +356 if let Some(forgotten) = space.sent(pn, packet) { +357 self.remove_in_flight(&forgotten); +358 } +359 } +360 +361 /// Remove `packet` with number `pn` from this path's congestion control counters, or return +362 /// `false` if `pn` was sent before this path was established. +363 pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool { +364 if packet.path_generation != self.generation { +365 return false; +366 } +367 self.in_flight.remove(packet); +368 true +369 } +370 +371 /// Increment the total size of sent UDP datagrams +372 pub(super) fn inc_total_sent(&mut self, inc: u64) { +373 self.total_sent = self.total_sent.saturating_add(inc); +374 if !self.validated { +375 trace!( +376 network_path = %self.network_path, +377 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), +378 "anti amplification budget decreased" +379 ); +380 } +381 } +382 +383 /// Increment the total size of received UDP datagrams +384 pub(super) fn inc_total_recvd(&mut self, inc: u64) { +385 self.total_recvd = self.total_recvd.saturating_add(inc); +386 if !self.validated { +387 trace!( +388 network_path = %self.network_path, +389 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), +390 "anti amplification budget increased" +391 ); +392 } +393 } +394 +395 /// The earliest time at which a sent challenge is considered lost. +396 pub(super) fn earliest_expiring_challenge(&self) -> Option<Instant> { +397 if self.challenges_sent.is_empty() { +398 return None; +399 } +400 let pto = self.rtt.pto_base(); +401 self.challenges_sent +402 .values() +403 .map(|info| info.sent_instant) +404 .min() +405 .map(|sent_instant| sent_instant + pto) +406 } +407 +408 /// Handle receiving a PATH_RESPONSE. +409 pub(super) fn on_path_response_received( +410 &mut self, +411 now: Instant, +412 token: u64, +413 network_path: FourTuple, +414 ) -> OnPathResponseReceived { +415 match self.challenges_sent.get(&token) { +416 // Response to an on-path PathChallenge +417 Some(info) +418 if info.network_path.is_probably_same_path(&network_path) +419 && self.network_path.is_probably_same_path(&network_path) => +420 { +421 self.network_path.update_local_if_same_remote(&network_path); +422 let sent_instant = info.sent_instant; +423 if !std::mem::replace(&mut self.validated, true) { +424 trace!("new path validated"); +425 } +426 // Clear any other on-path sent challenge. +427 self.challenges_sent +428 .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path)); +429 +430 self.send_new_challenge = false; +431 +432 // This RTT can only be used for the initial RTT, not as a normal +433 // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2. +434 let rtt = now.saturating_duration_since(sent_instant); +435 self.rtt.reset_initial_rtt(rtt); 436 -437 // This RTT can only be used for the initial RTT, not as a normal -438 // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2. -439 let rtt = now.saturating_duration_since(sent_instant); -440 self.rtt.reset_initial_rtt(rtt); -441 -442 let was_open = std::mem::replace(&mut self.open, true); -443 OnPathResponseReceived::OnPath { was_open } -444 } -445 // Response to an off-path PathChallenge -446 Some(info) if info.network_path.is_probably_same_path(&network_path) => { -447 self.challenges_sent -448 .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path)); -449 OnPathResponseReceived::OffPath -450 } -451 // Response to a PathChallenge we recognize, but from an invalid remote -452 Some(info) => OnPathResponseReceived::Invalid { -453 expected: info.network_path, -454 }, -455 // Response to an unknown PathChallenge -456 None => OnPathResponseReceived::Unknown, -457 } -458 } -459 -460 #[cfg(feature = "qlog")] -461 pub(super) fn qlog_recovery_metrics( -462 &mut self, -463 path_id: PathId, -464 ) -> Option<RecoveryMetricsUpdated> { -465 let controller_metrics = self.congestion.metrics(); -466 -467 let metrics = RecoveryMetrics { -468 min_rtt: Some(self.rtt.min), -469 smoothed_rtt: Some(self.rtt.get()), -470 latest_rtt: Some(self.rtt.latest), -471 rtt_variance: Some(self.rtt.var), -472 pto_count: Some(self.pto_count), -473 bytes_in_flight: Some(self.in_flight.bytes), -474 packets_in_flight: Some(self.in_flight.ack_eliciting), +437 let was_open = std::mem::replace(&mut self.open, true); +438 OnPathResponseReceived::OnPath { was_open } +439 } +440 // Response to an off-path PathChallenge +441 Some(info) if info.network_path.is_probably_same_path(&network_path) => { +442 self.challenges_sent +443 .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path)); +444 OnPathResponseReceived::OffPath +445 } +446 // Response to a PathChallenge we recognize, but from an invalid remote +447 Some(info) => OnPathResponseReceived::Invalid { +448 expected: info.network_path, +449 }, +450 // Response to an unknown PathChallenge +451 None => OnPathResponseReceived::Unknown, +452 } +453 } +454 +455 #[cfg(feature = "qlog")] +456 pub(super) fn qlog_recovery_metrics( +457 &mut self, +458 path_id: PathId, +459 ) -> Option<RecoveryMetricsUpdated> { +460 let controller_metrics = self.congestion.metrics(); +461 +462 let metrics = RecoveryMetrics { +463 min_rtt: Some(self.rtt.min), +464 smoothed_rtt: Some(self.rtt.get()), +465 latest_rtt: Some(self.rtt.latest), +466 rtt_variance: Some(self.rtt.var), +467 pto_count: Some(self.pto_count), +468 bytes_in_flight: Some(self.in_flight.bytes), +469 packets_in_flight: Some(self.in_flight.ack_eliciting), +470 +471 congestion_window: Some(controller_metrics.congestion_window), +472 ssthresh: controller_metrics.ssthresh, +473 pacing_rate: controller_metrics.pacing_rate, +474 }; 475 -476 congestion_window: Some(controller_metrics.congestion_window), -477 ssthresh: controller_metrics.ssthresh, -478 pacing_rate: controller_metrics.pacing_rate, -479 }; +476 let event = metrics.to_qlog_event(path_id, &self.recovery_metrics); +477 self.recovery_metrics = metrics; +478 event +479 } 480 -481 let event = metrics.to_qlog_event(path_id, &self.recovery_metrics); -482 self.recovery_metrics = metrics; -483 event -484 } -485 -486 /// Return how long we need to wait before sending `bytes_to_send` -487 /// -488 /// See [`Pacer::delay`]. -489 pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Instant> { -490 let smoothed_rtt = self.rtt.get(); -491 self.pacing.delay( -492 smoothed_rtt, -493 bytes_to_send, -494 self.current_mtu(), -495 self.congestion.window(), -496 now, -497 ) -498 } -499 -500 /// Updates the last observed address report received on this path. -501 /// -502 /// If the address was updated, it's returned to be informed to the application. -503 #[must_use = "updated observed address must be reported to the application"] -504 pub(super) fn update_observed_addr_report( -505 &mut self, -506 observed: ObservedAddr, -507 ) -> Option<SocketAddr> { -508 match self.last_observed_addr_report.as_mut() { -509 Some(prev) => { -510 if prev.seq_no >= observed.seq_no { -511 // frames that do not increase the sequence number on this path are ignored -512 None -513 } else if prev.ip == observed.ip && prev.port == observed.port { -514 // keep track of the last seq_no but do not report the address as updated -515 prev.seq_no = observed.seq_no; -516 None -517 } else { -518 let addr = observed.socket_addr(); -519 self.last_observed_addr_report = Some(observed); -520 Some(addr) -521 } +481 /// Return how long we need to wait before sending `bytes_to_send` +482 /// +483 /// See [`Pacer::delay`]. +484 pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Instant> { +485 let smoothed_rtt = self.rtt.get(); +486 self.pacing.delay( +487 smoothed_rtt, +488 bytes_to_send, +489 self.current_mtu(), +490 self.congestion.window(), +491 now, +492 ) +493 } +494 +495 /// Updates the last observed address report received on this path. +496 /// +497 /// If the address was updated, it's returned to be informed to the application. +498 #[must_use = "updated observed address must be reported to the application"] +499 pub(super) fn update_observed_addr_report( +500 &mut self, +501 observed: ObservedAddr, +502 ) -> Option<SocketAddr> { +503 match self.last_observed_addr_report.as_mut() { +504 Some(prev) => { +505 if prev.seq_no >= observed.seq_no { +506 // frames that do not increase the sequence number on this path are ignored +507 None +508 } else if prev.ip == observed.ip && prev.port == observed.port { +509 // keep track of the last seq_no but do not report the address as updated +510 prev.seq_no = observed.seq_no; +511 None +512 } else { +513 let addr = observed.socket_addr(); +514 self.last_observed_addr_report = Some(observed); +515 Some(addr) +516 } +517 } +518 None => { +519 let addr = observed.socket_addr(); +520 self.last_observed_addr_report = Some(observed); +521 Some(addr) 522 } -523 None => { -524 let addr = observed.socket_addr(); -525 self.last_observed_addr_report = Some(observed); -526 Some(addr) -527 } -528 } -529 } -530 -531 pub(crate) fn remote_status(&self) -> Option<PathStatus> { -532 self.status.remote_status.map(|(_seq, status)| status) -533 } -534 -535 pub(crate) fn local_status(&self) -> PathStatus { -536 self.status.local_status -537 } +523 } +524 } +525 +526 pub(crate) fn remote_status(&self) -> Option<PathStatus> { +527 self.status.remote_status.map(|(_seq, status)| status) +528 } +529 +530 pub(crate) fn local_status(&self) -> PathStatus { +531 self.status.local_status +532 } +533 +534 pub(super) fn generation(&self) -> u64 { +535 self.generation +536 } +537} 538 -539 pub(super) fn generation(&self) -> u64 { -540 self.generation -541 } -542} -543 -544pub(super) enum OnPathResponseReceived { -545 /// This response validates the path on its current remote address. -546 OnPath { was_open: bool }, -547 /// This response is valid, but it's for a remote other than the path's current remote address. -548 OffPath, -549 /// The received token is unknown. -550 Unknown, -551 /// The response is invalid. -552 Invalid { -553 /// The 4-tuple that was expected for this token. -554 expected: FourTuple, -555 }, -556} -557 -558/// Congestion metrics as described in [`recovery_metrics_updated`]. -559/// -560/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated -561#[cfg(feature = "qlog")] -562#[derive(Default, Clone, PartialEq, Debug)] -563#[non_exhaustive] -564struct RecoveryMetrics { -565 pub min_rtt: Option<Duration>, -566 pub smoothed_rtt: Option<Duration>, -567 pub latest_rtt: Option<Duration>, -568 pub rtt_variance: Option<Duration>, -569 pub pto_count: Option<u32>, -570 pub bytes_in_flight: Option<u64>, -571 pub packets_in_flight: Option<u64>, -572 pub congestion_window: Option<u64>, -573 pub ssthresh: Option<u64>, -574 pub pacing_rate: Option<u64>, -575} -576 -577#[cfg(feature = "qlog")] -578impl RecoveryMetrics { -579 /// Retain only values that have been updated since the last snapshot. -580 fn retain_updated(&self, previous: &Self) -> Self { -581 macro_rules! keep_if_changed { -582 ($name:ident) => { -583 if previous.$name == self.$name { -584 None -585 } else { -586 self.$name -587 } -588 }; -589 } -590 -591 Self { -592 min_rtt: keep_if_changed!(min_rtt), -593 smoothed_rtt: keep_if_changed!(smoothed_rtt), -594 latest_rtt: keep_if_changed!(latest_rtt), -595 rtt_variance: keep_if_changed!(rtt_variance), -596 pto_count: keep_if_changed!(pto_count), -597 bytes_in_flight: keep_if_changed!(bytes_in_flight), -598 packets_in_flight: keep_if_changed!(packets_in_flight), -599 congestion_window: keep_if_changed!(congestion_window), -600 ssthresh: keep_if_changed!(ssthresh), -601 pacing_rate: keep_if_changed!(pacing_rate), -602 } -603 } -604 -605 /// Emit a `MetricsUpdated` event containing only updated values -606 fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> { -607 let updated = self.retain_updated(previous); -608 -609 if updated == Self::default() { -610 return None; -611 } -612 -613 Some(RecoveryMetricsUpdated { -614 min_rtt: updated.min_rtt.map(|rtt| rtt.as_secs_f32()), -615 smoothed_rtt: updated.smoothed_rtt.map(|rtt| rtt.as_secs_f32()), -616 latest_rtt: updated.latest_rtt.map(|rtt| rtt.as_secs_f32()), -617 rtt_variance: updated.rtt_variance.map(|rtt| rtt.as_secs_f32()), -618 pto_count: updated -619 .pto_count -620 .map(|count| count.try_into().unwrap_or(u16::MAX)), -621 bytes_in_flight: updated.bytes_in_flight, -622 packets_in_flight: updated.packets_in_flight, -623 congestion_window: updated.congestion_window, -624 ssthresh: updated.ssthresh, -625 pacing_rate: updated.pacing_rate, -626 path_id: Some(path_id.as_u32() as u64), -627 }) -628 } -629} -630 -631/// RTT estimation for a particular network path -632#[derive(Copy, Clone, Debug)] -633pub struct RttEstimator { -634 /// The most recent RTT measurement made when receiving an ack for a previously unacked packet -635 latest: Duration, -636 /// The smoothed RTT of the connection, computed as described in RFC6298 -637 smoothed: Option<Duration>, -638 /// The RTT variance, computed as described in RFC6298 -639 var: Duration, -640 /// The minimum RTT seen in the connection, ignoring ack delay. -641 min: Duration, -642} -643 -644impl RttEstimator { -645 pub(super) fn new(initial_rtt: Duration) -> Self { -646 Self { -647 latest: initial_rtt, -648 smoothed: None, -649 var: initial_rtt / 2, -650 min: initial_rtt, -651 } -652 } -653 -654 /// Resets the estimator using a new initial_rtt value. -655 /// -656 /// This only resets the initial_rtt **if** no samples have been recorded yet. If there -657 /// are any recorded samples the initial estimate can not be adjusted after the fact. -658 /// -659 /// This is useful when you receive a PATH_RESPONSE in the first packet received on a -660 /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as -661 /// the initial RTT to get a better expected estimation. -662 /// -663 /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used -664 /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself -665 /// already. -666 pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) { -667 if self.smoothed.is_none() { -668 self.latest = initial_rtt; -669 self.var = initial_rtt / 2; -670 self.min = initial_rtt; -671 } +539pub(super) enum OnPathResponseReceived { +540 /// This response validates the path on its current remote address. +541 OnPath { was_open: bool }, +542 /// This response is valid, but it's for a remote other than the path's current remote address. +543 OffPath, +544 /// The received token is unknown. +545 Unknown, +546 /// The response is invalid. +547 Invalid { +548 /// The 4-tuple that was expected for this token. +549 expected: FourTuple, +550 }, +551} +552 +553/// Congestion metrics as described in [`recovery_metrics_updated`]. +554/// +555/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated +556#[cfg(feature = "qlog")] +557#[derive(Default, Clone, PartialEq, Debug)] +558#[non_exhaustive] +559struct RecoveryMetrics { +560 pub min_rtt: Option<Duration>, +561 pub smoothed_rtt: Option<Duration>, +562 pub latest_rtt: Option<Duration>, +563 pub rtt_variance: Option<Duration>, +564 pub pto_count: Option<u32>, +565 pub bytes_in_flight: Option<u64>, +566 pub packets_in_flight: Option<u64>, +567 pub congestion_window: Option<u64>, +568 pub ssthresh: Option<u64>, +569 pub pacing_rate: Option<u64>, +570} +571 +572#[cfg(feature = "qlog")] +573impl RecoveryMetrics { +574 /// Retain only values that have been updated since the last snapshot. +575 fn retain_updated(&self, previous: &Self) -> Self { +576 macro_rules! keep_if_changed { +577 ($name:ident) => { +578 if previous.$name == self.$name { +579 None +580 } else { +581 self.$name +582 } +583 }; +584 } +585 +586 Self { +587 min_rtt: keep_if_changed!(min_rtt), +588 smoothed_rtt: keep_if_changed!(smoothed_rtt), +589 latest_rtt: keep_if_changed!(latest_rtt), +590 rtt_variance: keep_if_changed!(rtt_variance), +591 pto_count: keep_if_changed!(pto_count), +592 bytes_in_flight: keep_if_changed!(bytes_in_flight), +593 packets_in_flight: keep_if_changed!(packets_in_flight), +594 congestion_window: keep_if_changed!(congestion_window), +595 ssthresh: keep_if_changed!(ssthresh), +596 pacing_rate: keep_if_changed!(pacing_rate), +597 } +598 } +599 +600 /// Emit a `MetricsUpdated` event containing only updated values +601 fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> { +602 let updated = self.retain_updated(previous); +603 +604 if updated == Self::default() { +605 return None; +606 } +607 +608 Some(RecoveryMetricsUpdated { +609 min_rtt: updated.min_rtt.map(|rtt| rtt.as_secs_f32()), +610 smoothed_rtt: updated.smoothed_rtt.map(|rtt| rtt.as_secs_f32()), +611 latest_rtt: updated.latest_rtt.map(|rtt| rtt.as_secs_f32()), +612 rtt_variance: updated.rtt_variance.map(|rtt| rtt.as_secs_f32()), +613 pto_count: updated +614 .pto_count +615 .map(|count| count.try_into().unwrap_or(u16::MAX)), +616 bytes_in_flight: updated.bytes_in_flight, +617 packets_in_flight: updated.packets_in_flight, +618 congestion_window: updated.congestion_window, +619 ssthresh: updated.ssthresh, +620 pacing_rate: updated.pacing_rate, +621 path_id: Some(path_id.as_u32() as u64), +622 }) +623 } +624} +625 +626/// RTT estimation for a particular network path +627#[derive(Copy, Clone, Debug)] +628pub struct RttEstimator { +629 /// The most recent RTT measurement made when receiving an ack for a previously unacked packet +630 latest: Duration, +631 /// The smoothed RTT of the connection, computed as described in RFC6298 +632 smoothed: Option<Duration>, +633 /// The RTT variance, computed as described in RFC6298 +634 var: Duration, +635 /// The minimum RTT seen in the connection, ignoring ack delay. +636 min: Duration, +637} +638 +639impl RttEstimator { +640 pub(super) fn new(initial_rtt: Duration) -> Self { +641 Self { +642 latest: initial_rtt, +643 smoothed: None, +644 var: initial_rtt / 2, +645 min: initial_rtt, +646 } +647 } +648 +649 /// Resets the estimator using a new initial_rtt value. +650 /// +651 /// This only resets the initial_rtt **if** no samples have been recorded yet. If there +652 /// are any recorded samples the initial estimate can not be adjusted after the fact. +653 /// +654 /// This is useful when you receive a PATH_RESPONSE in the first packet received on a +655 /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as +656 /// the initial RTT to get a better expected estimation. +657 /// +658 /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used +659 /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself +660 /// already. +661 pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) { +662 if self.smoothed.is_none() { +663 self.latest = initial_rtt; +664 self.var = initial_rtt / 2; +665 self.min = initial_rtt; +666 } +667 } +668 +669 /// The current best RTT estimation. +670 pub fn get(&self) -> Duration { +671 self.smoothed.unwrap_or(self.latest) 672 } 673 -674 /// The current best RTT estimation. -675 pub fn get(&self) -> Duration { -676 self.smoothed.unwrap_or(self.latest) -677 } -678 -679 /// Conservative estimate of RTT -680 /// -681 /// Takes the maximum of smoothed and latest RTT, as recommended -682 /// in 6.1.2 of the recovery spec (draft 29). -683 pub fn conservative(&self) -> Duration { -684 self.get().max(self.latest) +674 /// Conservative estimate of RTT +675 /// +676 /// Takes the maximum of smoothed and latest RTT, as recommended +677 /// in 6.1.2 of the recovery spec (draft 29). +678 pub fn conservative(&self) -> Duration { +679 self.get().max(self.latest) +680 } +681 +682 /// Minimum RTT registered so far for this estimator. +683 pub fn min(&self) -> Duration { +684 self.min 685 } 686 -687 /// Minimum RTT registered so far for this estimator. -688 pub fn min(&self) -> Duration { -689 self.min +687 /// PTO computed as described in RFC9002#6.2.1. +688 pub(crate) fn pto_base(&self) -> Duration { +689 self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY) 690 } 691 -692 /// PTO computed as described in RFC9002#6.2.1. -693 pub(crate) fn pto_base(&self) -> Duration { -694 self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY) -695 } -696 -697 /// Records an RTT sample. -698 pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) { -699 self.latest = rtt; -700 // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3: -701 // min_rtt does not adjust for ack_delay to avoid underestimating. -702 self.min = cmp::min(self.min, self.latest); -703 // Based on RFC6298. -704 if let Some(smoothed) = self.smoothed { -705 let adjusted_rtt = if self.min + ack_delay <= self.latest { -706 self.latest - ack_delay -707 } else { -708 self.latest -709 }; -710 let var_sample = smoothed.abs_diff(adjusted_rtt); -711 self.var = (3 * self.var + var_sample) / 4; -712 self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8); -713 } else { -714 self.smoothed = Some(self.latest); -715 self.var = self.latest / 2; -716 self.min = self.latest; -717 } -718 } +692 /// Records an RTT sample. +693 pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) { +694 self.latest = rtt; +695 // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3: +696 // min_rtt does not adjust for ack_delay to avoid underestimating. +697 self.min = cmp::min(self.min, self.latest); +698 // Based on RFC6298. +699 if let Some(smoothed) = self.smoothed { +700 let adjusted_rtt = if self.min + ack_delay <= self.latest { +701 self.latest - ack_delay +702 } else { +703 self.latest +704 }; +705 let var_sample = smoothed.abs_diff(adjusted_rtt); +706 self.var = (3 * self.var + var_sample) / 4; +707 self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8); +708 } else { +709 self.smoothed = Some(self.latest); +710 self.var = self.latest / 2; +711 self.min = self.latest; +712 } +713 } +714} +715 +716#[derive(Default, Debug)] +717pub(crate) struct PathResponses { +718 pending: Vec<PathResponse>, 719} 720 -721#[derive(Default, Debug)] -722pub(crate) struct PathResponses { -723 pending: Vec<PathResponse>, -724} -725 -726impl PathResponses { -727 pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) { -728 /// Arbitrary permissive limit to prevent abuse -729 const MAX_PATH_RESPONSES: usize = 16; -730 let response = PathResponse { -731 packet, -732 token, -733 network_path, -734 }; -735 let existing = self -736 .pending -737 .iter_mut() -738 .find(|x| x.network_path.remote == network_path.remote); -739 if let Some(existing) = existing { -740 // Update a queued response -741 if existing.packet <= packet { -742 *existing = response; -743 } -744 return; -745 } -746 if self.pending.len() < MAX_PATH_RESPONSES { -747 self.pending.push(response); -748 } else { -749 // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping -750 // older challenges. -751 trace!("ignoring excessive PATH_CHALLENGE"); -752 } -753 } -754 -755 pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> { -756 let response = *self.pending.last()?; -757 // We use an exact comparison here, because once we've received for the first time, -758 // we really should either already have a local_ip, or we will never get one -759 // (because our OS doesn't support it). -760 if response.network_path == network_path { -761 // We don't bother searching further because we expect that the on-path response will -762 // get drained in the immediate future by a call to `pop_on_path` -763 return None; -764 } -765 self.pending.pop(); -766 Some((response.token, response.network_path)) -767 } -768 -769 pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> { -770 let response = *self.pending.last()?; -771 // Using an exact comparison. See explanation in `pop_off_path`. -772 if response.network_path != network_path { -773 // We don't bother searching further because we expect that the off-path response will -774 // get drained in the immediate future by a call to `pop_off_path` -775 return None; -776 } -777 self.pending.pop(); -778 Some(response.token) -779 } +721impl PathResponses { +722 pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) { +723 /// Arbitrary permissive limit to prevent abuse +724 const MAX_PATH_RESPONSES: usize = 16; +725 let response = PathResponse { +726 packet, +727 token, +728 network_path, +729 }; +730 let existing = self +731 .pending +732 .iter_mut() +733 .find(|x| x.network_path.remote == network_path.remote); +734 if let Some(existing) = existing { +735 // Update a queued response +736 if existing.packet <= packet { +737 *existing = response; +738 } +739 return; +740 } +741 if self.pending.len() < MAX_PATH_RESPONSES { +742 self.pending.push(response); +743 } else { +744 // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping +745 // older challenges. +746 trace!("ignoring excessive PATH_CHALLENGE"); +747 } +748 } +749 +750 pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> { +751 let response = *self.pending.last()?; +752 // We use an exact comparison here, because once we've received for the first time, +753 // we really should either already have a local_ip, or we will never get one +754 // (because our OS doesn't support it). +755 if response.network_path == network_path { +756 // We don't bother searching further because we expect that the on-path response will +757 // get drained in the immediate future by a call to `pop_on_path` +758 return None; +759 } +760 self.pending.pop(); +761 Some((response.token, response.network_path)) +762 } +763 +764 pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> { +765 let response = *self.pending.last()?; +766 // Using an exact comparison. See explanation in `pop_off_path`. +767 if response.network_path != network_path { +768 // We don't bother searching further because we expect that the off-path response will +769 // get drained in the immediate future by a call to `pop_off_path` +770 return None; +771 } +772 self.pending.pop(); +773 Some(response.token) +774 } +775 +776 pub(crate) fn is_empty(&self) -> bool { +777 self.pending.is_empty() +778 } +779} 780 -781 pub(crate) fn is_empty(&self) -> bool { -782 self.pending.is_empty() -783 } -784} -785 -786#[derive(Copy, Clone, Debug)] -787struct PathResponse { -788 /// The packet number the corresponding PATH_CHALLENGE was received in -789 packet: u64, -790 /// The token of the PATH_CHALLENGE -791 token: u64, -792 /// The path the corresponding PATH_CHALLENGE was received from -793 network_path: FourTuple, -794} -795 -796/// Summary statistics of packets that have been sent on a particular path, but which have not yet -797/// been acked or deemed lost -798#[derive(Debug)] -799pub(super) struct InFlight { -800 /// Sum of the sizes of all sent packets considered "in flight" by congestion control +781#[derive(Copy, Clone, Debug)] +782struct PathResponse { +783 /// The packet number the corresponding PATH_CHALLENGE was received in +784 packet: u64, +785 /// The token of the PATH_CHALLENGE +786 token: u64, +787 /// The path the corresponding PATH_CHALLENGE was received from +788 network_path: FourTuple, +789} +790 +791/// Summary statistics of packets that have been sent on a particular path, but which have not yet +792/// been acked or deemed lost +793#[derive(Debug)] +794pub(super) struct InFlight { +795 /// Sum of the sizes of all sent packets considered "in flight" by congestion control +796 /// +797 /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not +798 /// count towards this to ensure congestion control does not impede congestion feedback. +799 pub(super) bytes: u64, +800 /// Number of packets in flight containing frames other than ACK and PADDING 801 /// -802 /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not -803 /// count towards this to ensure congestion control does not impede congestion feedback. -804 pub(super) bytes: u64, -805 /// Number of packets in flight containing frames other than ACK and PADDING -806 /// -807 /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be -808 /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always -809 /// also be nonzero. -810 pub(super) ack_eliciting: u64, -811} -812 -813impl InFlight { -814 fn new() -> Self { -815 Self { -816 bytes: 0, -817 ack_eliciting: 0, -818 } +802 /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be +803 /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always +804 /// also be nonzero. +805 pub(super) ack_eliciting: u64, +806} +807 +808impl InFlight { +809 fn new() -> Self { +810 Self { +811 bytes: 0, +812 ack_eliciting: 0, +813 } +814 } +815 +816 fn insert(&mut self, packet: &SentPacket) { +817 self.bytes += u64::from(packet.size); +818 self.ack_eliciting += u64::from(packet.ack_eliciting); 819 } 820 -821 fn insert(&mut self, packet: &SentPacket) { -822 self.bytes += u64::from(packet.size); -823 self.ack_eliciting += u64::from(packet.ack_eliciting); -824 } -825 -826 /// Update counters to account for a packet becoming acknowledged, lost, or abandoned -827 fn remove(&mut self, packet: &SentPacket) { -828 self.bytes -= u64::from(packet.size); -829 self.ack_eliciting -= u64::from(packet.ack_eliciting); -830 } -831} -832 -833/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames -834#[derive(Debug, Clone, Default)] -835pub(super) struct PathStatusState { -836 /// The local status -837 local_status: PathStatus, -838 /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP -839 /// -840 /// This is the number of the *next* path status frame to be sent. -841 local_seq: VarInt, -842 /// The status set by the remote -843 remote_status: Option<(VarInt, PathStatus)>, -844} -845 -846impl PathStatusState { -847 /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames -848 pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) { -849 if self.remote_status.is_some_and(|(curr, _)| curr >= seq) { -850 return trace!(%seq, "ignoring path status update"); +821 /// Update counters to account for a packet becoming acknowledged, lost, or abandoned +822 fn remove(&mut self, packet: &SentPacket) { +823 self.bytes -= u64::from(packet.size); +824 self.ack_eliciting -= u64::from(packet.ack_eliciting); +825 } +826} +827 +828/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames +829#[derive(Debug, Clone, Default)] +830pub(super) struct PathStatusState { +831 /// The local status +832 local_status: PathStatus, +833 /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP +834 /// +835 /// This is the number of the *next* path status frame to be sent. +836 local_seq: VarInt, +837 /// The status set by the remote +838 remote_status: Option<(VarInt, PathStatus)>, +839} +840 +841impl PathStatusState { +842 /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames +843 pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) { +844 if self.remote_status.is_some_and(|(curr, _)| curr >= seq) { +845 return trace!(%seq, "ignoring path status update"); +846 } +847 +848 let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s); +849 if prev != Some(status) { +850 debug!(?status, ?seq, "remote changed path status"); 851 } -852 -853 let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s); -854 if prev != Some(status) { -855 debug!(?status, ?seq, "remote changed path status"); -856 } -857 } -858 -859 /// Updates the local status -860 /// -861 /// If the local status changed, the previous value is returned -862 pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> { -863 if self.local_status == status { -864 return None; -865 } -866 -867 self.local_seq = self.local_seq.saturating_add(1u8); -868 Some(std::mem::replace(&mut self.local_status, status)) -869 } +852 } +853 +854 /// Updates the local status +855 /// +856 /// If the local status changed, the previous value is returned +857 pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> { +858 if self.local_status == status { +859 return None; +860 } +861 +862 self.local_seq = self.local_seq.saturating_add(1u8); +863 Some(std::mem::replace(&mut self.local_status, status)) +864 } +865 +866 pub(crate) fn seq(&self) -> VarInt { +867 self.local_seq +868 } +869} 870 -871 pub(crate) fn seq(&self) -> VarInt { -872 self.local_seq -873 } -874} -875 -876/// The QUIC-MULTIPATH path status -877/// -878/// See section "3.3 Path Status Management": -879/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management> -880#[cfg_attr(test, derive(test_strategy::Arbitrary))] -881#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] -882pub enum PathStatus { -883 /// Paths marked with as available will be used when scheduling packets -884 /// -885 /// If multiple paths are available, packets will be scheduled on whichever has -886 /// capacity. -887 #[default] -888 Available, -889 /// Paths marked as backup will only be used if there are no available paths -890 /// -891 /// If the max_idle_timeout is specified the path will be kept alive so that it does not -892 /// expire. -893 Backup, -894} -895 -896/// Application events about paths -897#[derive(Debug, Clone, PartialEq, Eq)] -898pub enum PathEvent { -899 /// A new path has been opened -900 Opened { -901 /// Which path is now open +871/// The QUIC-MULTIPATH path status +872/// +873/// See section "3.3 Path Status Management": +874/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management> +875#[cfg_attr(test, derive(test_strategy::Arbitrary))] +876#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +877pub enum PathStatus { +878 /// Paths marked with as available will be used when scheduling packets +879 /// +880 /// If multiple paths are available, packets will be scheduled on whichever has +881 /// capacity. +882 #[default] +883 Available, +884 /// Paths marked as backup will only be used if there are no available paths +885 /// +886 /// If the max_idle_timeout is specified the path will be kept alive so that it does not +887 /// expire. +888 Backup, +889} +890 +891/// Application events about paths +892#[derive(Debug, Clone, PartialEq, Eq)] +893pub enum PathEvent { +894 /// A new path has been opened +895 Opened { +896 /// Which path is now open +897 id: PathId, +898 }, +899 /// The path was abandoned and all remaining state for it has been removed. +900 Abandoned { +901 /// Which path had its state dropped 902 id: PathId, -903 }, -904 /// The path was abandoned and all remaining state for it has been removed. -905 Abandoned { -906 /// Which path had its state dropped -907 id: PathId, -908 /// The final path stats, they are no longer available via [`Connection::stats`] -909 /// -910 /// [`Connection::stats`]: super::Connection::stats -911 path_stats: PathStats, -912 }, -913 /// Path was closed locally -914 LocallyClosed { -915 /// Path for which the error occurred -916 id: PathId, -917 /// The error that occurred -918 error: PathError, -919 }, -920 /// The remote changed the status of the path -921 /// -922 /// The local status is not changed because of this event. It is up to the application -923 /// to update the local status, which is used for packet scheduling, when the remote -924 /// changes the status. -925 RemoteStatus { -926 /// Path which has changed status -927 id: PathId, -928 /// The new status set by the remote -929 status: PathStatus, -930 }, -931 /// Received an observation of our external address from the peer. -932 ObservedAddr { -933 /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is -934 /// not negotiated -935 id: PathId, -936 /// The address observed by the remote over this path -937 addr: SocketAddr, -938 }, -939} -940 -941/// Error from setting path status -942#[derive(Debug, Error, Clone, PartialEq, Eq)] -943pub enum SetPathStatusError { -944 /// Error indicating that a path has not been opened or has already been abandoned -945 #[error("closed path")] -946 ClosedPath, -947 /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been -948 #[error("multipath not negotiated")] -949 MultipathNotNegotiated, -950} -951 -952/// Error indicating that a path has not been opened or has already been abandoned -953#[derive(Debug, Default, Error, Clone, PartialEq, Eq)] -954#[error("closed path")] -955pub struct ClosedPath { -956 pub(super) _private: (), -957} -958 -959#[cfg(test)] -960mod tests { -961 use super::*; -962 -963 #[test] -964 fn test_path_id_saturating_add() { -965 // add within range behaves normally -966 let large: PathId = u16::MAX.into(); -967 let next = u32::from(u16::MAX) + 1; -968 assert_eq!(large.saturating_add(1u8), PathId::from(next)); -969 -970 // outside range saturates -971 assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX) -972 } -973}

\ No newline at end of file +903 /// The final path stats, they are no longer available via [`Connection::stats`] +904 /// +905 /// [`Connection::stats`]: super::Connection::stats +906 path_stats: PathStats, +907 }, +908 /// Path was closed locally +909 LocallyClosed { +910 /// Path for which the error occurred +911 id: PathId, +912 /// The error that occurred +913 error: PathError, +914 }, +915 /// The remote changed the status of the path +916 /// +917 /// The local status is not changed because of this event. It is up to the application +918 /// to update the local status, which is used for packet scheduling, when the remote +919 /// changes the status. +920 RemoteStatus { +921 /// Path which has changed status +922 id: PathId, +923 /// The new status set by the remote +924 status: PathStatus, +925 }, +926 /// Received an observation of our external address from the peer. +927 ObservedAddr { +928 /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is +929 /// not negotiated +930 id: PathId, +931 /// The address observed by the remote over this path +932 addr: SocketAddr, +933 }, +934} +935 +936/// Error from setting path status +937#[derive(Debug, Error, Clone, PartialEq, Eq)] +938pub enum SetPathStatusError { +939 /// Error indicating that a path has not been opened or has already been abandoned +940 #[error("closed path")] +941 ClosedPath, +942 /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been +943 #[error("multipath not negotiated")] +944 MultipathNotNegotiated, +945} +946 +947/// Error indicating that a path has not been opened or has already been abandoned +948#[derive(Debug, Default, Error, Clone, PartialEq, Eq)] +949#[error("closed path")] +950pub struct ClosedPath { +951 pub(super) _private: (), +952} +953 +954#[cfg(test)] +955mod tests { +956 use super::*; +957 +958 #[test] +959 fn test_path_id_saturating_add() { +960 // add within range behaves normally +961 let large: PathId = u16::MAX.into(); +962 let next = u32::from(u16::MAX) + 1; +963 assert_eq!(large.saturating_add(1u8), PathId::from(next)); +964 +965 // outside range saturates +966 assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX) +967 } +968}
\ No newline at end of file