A lot
This commit is contained in:
parent
c2a7afe6c1
commit
ade0c3cde4
24 changed files with 1701 additions and 321 deletions
92
CONNECTOR.md
92
CONNECTOR.md
|
|
@ -1,10 +1,10 @@
|
|||
# Connector
|
||||
|
||||
This file documents the Connection and Version Negotiation logic.
|
||||
This file documents the connection and version negotiation logic.
|
||||
|
||||
## Registry
|
||||
|
||||
The `registry` crate provides a multi-version `Registry` used by the host for version negotiation. Accessed through the `mtp` facade (requires the `host` feature):
|
||||
The `registry` module provides a multi-version `Registry` used by the host for version negotiation. Accessed through the `mtp` facade (requires the `host` feature):
|
||||
|
||||
```rust
|
||||
use mtp::codec::registry::Registry;
|
||||
|
|
@ -33,11 +33,12 @@ The host creates a QUIC server, manages the registry, and handles version negoti
|
|||
|
||||
### Initialization
|
||||
|
||||
The host binds to the address from the `mtp_BIND` environment variable (defaults to `::`) on the specified port:
|
||||
|
||||
```rust
|
||||
use mtp::host::{MTPHost, HostConfig};
|
||||
|
||||
let config = HostConfig {
|
||||
ip: "::".into(),
|
||||
port: 4433,
|
||||
tls_fullchain: std::fs::read("cert.pem")?,
|
||||
tls_key: std::fs::read("key.pem")?,
|
||||
|
|
@ -50,35 +51,31 @@ let mut host = MTPHost::new(config).await?;
|
|||
|
||||
```rust
|
||||
while let Some(conn) = host.accept().await {
|
||||
/*
|
||||
* conn.version is the negotiated version
|
||||
* conn.codec is a VersionedCodec scoped to that version
|
||||
* conn.sender / conn.receiver for raw CommunicationValue I/O
|
||||
*/
|
||||
// conn.version is the negotiated version
|
||||
// conn.codec is a VersionedCodec scoped to that version
|
||||
// conn.sender / conn.receiver for raw CommunicationValue I/O
|
||||
|
||||
let msg = conn.receiver.receive().await?;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The host's `accept()` method:
|
||||
1. Accepts a QUIC connection
|
||||
2. Reads the first `CommunicationValue` (always encoded with reserved type IDs)
|
||||
3. Extracts the client's protocol version from `DataType::Version` (wire ID 3)
|
||||
4. Calls `registry.negotiate(&[client_version])`
|
||||
5. Returns `None` if the version is unsupported (caller sends `ErrorBadVersion` and disconnects)
|
||||
6. Returns an `MTPConnection` with the negotiated version otherwise
|
||||
2. If authentication is required (crypto feature): performs login/register handshake
|
||||
3. Reads the first `CommunicationValue` (always encoded with reserved type IDs)
|
||||
4. Extracts the client's protocol version from `DataType::Version` (wire ID 3)
|
||||
5. Calls `registry.negotiate(&[client_version])`
|
||||
6. Returns `None` if the version is unsupported
|
||||
7. Returns an `MTPConnection` with the negotiated version otherwise
|
||||
|
||||
### Login/Register Flow
|
||||
### Login/Register Handshake (crypto feature)
|
||||
|
||||
The complete login/register handshake (see design docs) builds on top of `MTPConnection`:
|
||||
When `require_authentication` is set, the host sends a **greeting** first (host ID, public keys, nonce). The client then responds with either:
|
||||
|
||||
1. Client sends `Identification` with version, ID, nonce, signature
|
||||
2. Host verifies signature via `get_key` callback
|
||||
3. Host responds with approval + nonces + signature
|
||||
4. Client verifies response
|
||||
- **Login** (`CommunicationType::Identification`, ID 15): client ID, nonce, signature
|
||||
- **Register** (`CommunicationType::Register`, ID 17): public keys, nonce, signature
|
||||
|
||||
New clients use the `Register` variant instead, presenting their public key for registration.
|
||||
The host verifies the client's signature, sends a signed response, and the client verifies the host's signature.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -94,13 +91,14 @@ let config = ClientConfig {
|
|||
server_cert: None, // or Some(cert_pem_bytes)
|
||||
};
|
||||
|
||||
// Connect (existing client)
|
||||
// Connect (unauthenticated, existing client)
|
||||
let conn = MTPClient::connect(config, 8765).await?;
|
||||
|
||||
/*
|
||||
* conn.version is the compiled-in PROTOCOL_VERSION
|
||||
* conn.sender / conn.receiver for I/O
|
||||
*/
|
||||
// Authenticated login
|
||||
let conn = MTPClient::auth_connect(config, 8765, keys, host_pk).await?;
|
||||
|
||||
// Registration (new client)
|
||||
let conn = MTPClient::auth_register(config, keys, host_pk).await?;
|
||||
```
|
||||
|
||||
The client's `PROTOCOL_VERSION` constant is set by `protocol_version` in `type-maps.yaml` and baked in at compile time. The client never imports the `registry` crate; it only uses `mtp::type_map` for enum types and `mtp::codec` for encoding.
|
||||
|
|
@ -111,26 +109,26 @@ The client's `PROTOCOL_VERSION` constant is set by `protocol_version` in `type-m
|
|||
|
||||
```
|
||||
Client (v2.0) Host (v0.0, v1.0, v2.0)
|
||||
│ │
|
||||
│ QUIC connect │
|
||||
│───────────────────────→│
|
||||
│ │
|
||||
│ CommValue{ Ident. } │
|
||||
│ Version → "2.0" │
|
||||
│ Id → 8765 │
|
||||
│ Nonce → ... │
|
||||
│ Signature → ... │
|
||||
│───────────────────────→│
|
||||
│ │ registry.negotiate(&[Version(2,0)])
|
||||
│ │ → Some(Version(2,0))
|
||||
│ │
|
||||
│ Response │
|
||||
│←───────────────────────│ (uses v2.0 TypeMap for encoding)
|
||||
│ Status, Nonces, │
|
||||
│ Signature │
|
||||
│ │
|
||||
│ (subsequent messages │
|
||||
│ use v2.0 TypeMap) │
|
||||
| |
|
||||
| QUIC connect |
|
||||
|----------------------->|
|
||||
| |
|
||||
| CommValue{ Ident. } |
|
||||
| Version -> "2.0" |
|
||||
| Id -> 8765 |
|
||||
| Nonce -> ... |
|
||||
| Signature -> ... |
|
||||
|----------------------->|
|
||||
| | registry.negotiate(&[Version(2,0)])
|
||||
| | -> Some(Version(2,0))
|
||||
| |
|
||||
| Response |
|
||||
|<-----------------------| (uses v2.0 TypeMap for encoding)
|
||||
| Status, Nonces, |
|
||||
| Signature |
|
||||
| |
|
||||
| (subsequent messages |
|
||||
| use v2.0 TypeMap) |
|
||||
```
|
||||
|
||||
If the client sends an unsupported version (e.g. v3.0 when the host only knows up to v2.0), `negotiate` returns `None` and the host sends `ErrorBadVersion` using reserved types.
|
||||
If the client sends an unsupported version (e.g. v3.0 when the host only knows up to v2.0), `negotiate` returns `None` and the connection is closed.
|
||||
|
|
|
|||
Loading…
Reference in a new issue