docs/transit.md: remove Blocking section, expand on relay hints

These docs were first written back when we had both a blocking and a
non-blocking API. I removed all the blocking code a long time ago, leaving on
the Twisted-based non-blocking API, but I forgot to update the docs to match,
which is why some of the text didn't make much sense.
This commit is contained in:
Brian Warner 2020-01-14 09:58:08 -08:00
parent 9b305169ed
commit c5386bc7ac

View File

@ -7,7 +7,8 @@ delivered by Wormhole).
The protocol tries hard to create a **direct** connection between the two
ends, but if that fails, it uses a centralized relay server to ferry data
between two separate TCP streams (one to each client).
between two separate TCP streams (one to each client). Direct connection
hints are used for the first, and relay hints are used for the second.
The current implementation starts with the following:
@ -58,7 +59,7 @@ In addition, an active attacker is able to:
If either side receives a corrupted or out-of-order record, they drop the
connection. Attackers cannot modify the contents of a record, or change the
order of the records, without being detected and the connection being
dropped. If a record is lost (e.g. the receiver observers records #1,#2,#4,
dropped. If a record is lost (e.g. the receiver observer records #1,#2,#4,
but not #3), the connection is dropped when the unexpected sequence number is
received.
@ -111,6 +112,11 @@ two handshakes), then making new connections to play back the recorded
handshakes, but this level of attacker could simply drop the user's packets
directly.
Any participant in a Transit connection (i.e. the party on the other end of
your wormhole) can cause their peer to make a TCP connection (and send the
handshake string) to any IP address and port of their choosing. The handshake
protocol is intended to make this no more than a minor nuisance.
## Relay
The **Transit Relay** is a host which offers TURN-like services for
@ -139,27 +145,62 @@ attempting to use the relay. If it has no viable direct hints, it will start
using the relay right away. This prefers direct connections, but doesn't
introduce completely unnecessary stalls.
The Transit client can attempt connections to multiple relays, and uses the
first one that passes negotiation. Each side combines a locally-configured
hostname/port (usually "baked in" to the application, and hosted by the
application author) with additional hostname/port pairs that come from the
peer. This way either side can suggest the relays to use. The `wormhole`
application accepts a `--transit-helper tcp:myrelay.example.org:12345`
command-line option to supply an additional relay. The connection hints
provided by the Transit instance include the locally-configured relay, along
with the dynamically-determined direct hints. Both should be delivered to the
peer.
## API
The Transit API uses Twisted and returns Deferreds for any call that cannot
be handled immediately. The complete example is here:
```python
from twisted.internet.defer import inlineCallbacks
from wormhole.transit import TransitSender
@inlineCallbacks
def do_transit():
s = TransitSender("tcp:relayhost.example.org:12345")
my_connection_hints = yield s.get_connection_hints()
# (send my hints via wormhole)
# (get their hints via wormhole)
s.add_connection_hints(their_connection_hints)
key = w.derive_key(application_id + "/transit-key")
s.set_transit_key(key)
rp = yield s.connect()
rp.send_record(b"my first record")
their_record = yield rp.receive_record()
rp.send_record(b"Greatest Hits)
other = yield rp.receive_record()
yield rp.close()
```
First, create a Transit instance, giving it the connection information of the
transit relay. The application must know whether it should use a Sender or a
Receiver:
```python
from wormhole.blocking.transit import TransitSender
s = TransitSender("tcp:relayhost.example.org:12345")
from wormhole.transit import TransitSender
s = TransitSender()
```
Next, ask the Transit for its direct and relay hints. This should be
delivered to the other side via a Wormhole message (i.e. add them to a dict,
serialize it with JSON, send the result as a message with `wormhole.send()`).
serialize it with JSON, send the result as a message with `wormhole.send()`).
```python
connection_hints = yield s.get_connection_hints()
my_connection_hints = yield s.get_connection_hints()
```
The `get_connection_hints` method returns a Deferred, so in the example we are
using `@inlineCallbacks` to `yield` the result.
The `get_connection_hints` method returns a Deferred, so in the example we
use `@inlineCallbacks` to `yield` the result.
Then, perform the Wormhole exchange, which ought to give you the direct and
relay hints of the other side. Tell your Transit instance about their hints.
@ -178,54 +219,29 @@ key = w.derive_key(application_id + "/transit-key")
s.set_transit_key(key)
```
Finally, tell the Transit instance to connect. This will yield a "record
pipe" object, on which records can be sent and received. If no connection can
be established within a timeout (defaults to 30 seconds), `connect()` will
throw an exception instead. The pipe can be closed with `close()`.
Finally, tell the Transit instance to connect. This returns a Deferred that
will yield a "record pipe" object, on which records can be sent and received.
If no connection can be established within a timeout (defaults to 30
seconds), `connect()` will signal a Failure instead. The pipe can be closed
with `close()`, which returns a Deferred that fires when all data has been
flushed.
```python
rp = s.connect()
rp = yield s.connect()
rp.send_record(b"my first record")
their_record = rp.receive_record()
their_record = yield rp.receive_record()
rp.send_record(b"Greatest Hits)
other = rp.receive_record()
rp.close()
other = yield rp.receive_record()
yield rp.close()
```
Records can be sent and received arbitrarily (you are not limited to taking
turns). However the blocking API does not provide a way to send records while
waiting for an inbound record. This *might* work with threads, but it has not
been tested.
turns).
## Twisted API
The same facilities are available in the asynchronous Twisted environment.
The difference is that some functions return Deferreds instead of immediate
values. The final record-pipe object is a Protocol (TBD: maybe this is a job
for Tubes?), which exposes `receive_record()` as a Deferred-returning
function that internally holds a queue of inbound records.
```python
from twisted.internet.defer import inlineCallbacks
from wormhole.twisted.transit import TransitSender
@inlineCallbacks
def do_transit():
s = TransitSender(relay)
my_connection_hints = yield s.get_connection_hints()
# (send hints via wormhole)
s.add_connection_hints(their_connection_hints)
s.set_transit_key(key)
rp = yield s.connect()
rp.send_record(b"eponymous")
them = yield rp.receive_record()
yield rp.close()
```
This object also implements the `IConsumer`/`IProducer` protocols for
**bytes**, which means you can transfer a file by wiring up a file reader as
a Producer. Each chunk of bytes that the Producer generates will be put into
a single record. The Consumer interface works the same way. This enables
The record-pipe object also implements the `IConsumer`/`IProducer` protocols
for **bytes**, which means you can transfer a file by wiring up a file reader
as a Producer. Each chunk of bytes that the Producer generates will be put
into a single record. The Consumer interface works the same way. This enables
backpressure and flow-control: if the far end (or the network) cannot keep up
with the stream of data, the sender will wait for them to catch up before
filling buffers without bound.