INCOMPATIBILITY: send "transit" message before offer/answer

In the future, both sides should expect to receive "transit" messages at
any time, and they will add to the list of hints that they should try.
For now, each side only sends a single transit message, before they send
the offer (sender) or answer (receiver).
This commit is contained in:
Brian Warner 2016-05-24 18:59:04 -07:00
parent 1a9e565fc3
commit 812fd0b4da
2 changed files with 104 additions and 73 deletions

View File

@ -31,6 +31,7 @@ class TwistedReceiver:
self.args = args
self._reactor = reactor
self._tor_manager = None
self._transit_receiver = None
def msg(self, *args, **kwargs):
print(*args, file=self.args.stdout, **kwargs)
@ -75,45 +76,39 @@ class TwistedReceiver:
while True:
try:
them_d = yield self.get_data(w)
them_d = yield self._get_data(w)
except WormholeClosedError:
if done:
returnValue(None)
raise TransferError("unexpected close")
#print("GOT", them_d)
if u"transit" in them_d:
yield self._parse_transit(them_d[u"transit"], w)
continue
if u"offer" in them_d:
if not want_offer:
raise TransferError("duplicate offer")
try:
self.parse_offer(them_d[u"offer"], w)
yield self.parse_offer(them_d[u"offer"], w)
except RespondError as r:
data = json.dumps({"error": r.response}).encode("utf-8")
w.send(data)
self._send_data({"error": r.response}, w)
raise TransferError(r.response)
returnValue(None)
log.msg("unrecognized message %r" % (them_d,))
raise TransferError("expected offer, got none")
def _send_data(self, data, w):
data_bytes = json.dumps(data).encode("utf-8")
w.send(data_bytes)
@inlineCallbacks
def parse_offer(self, them_d, w):
if "message" in them_d:
self.handle_text(them_d, w)
returnValue(None)
if "file" in them_d:
f = self.handle_file(them_d)
rp = yield self.establish_transit(w, them_d)
yield self.transfer_data(rp, f)
self.write_file(f)
yield self.close_transit(rp)
elif "directory" in them_d:
f = self.handle_directory(them_d)
rp = yield self.establish_transit(w, them_d)
yield self.transfer_data(rp, f)
self.write_directory(f)
yield self.close_transit(rp)
else:
self.msg(u"I don't know what they're offering\n")
self.msg(u"Offer details: %r" % (them_d,))
raise RespondError("unknown offer type")
def _get_data(self, w):
# this may raise WrongPasswordError
them_bytes = yield w.get()
them_d = json.loads(them_bytes.decode("utf-8"))
if "error" in them_d:
raise TransferError(them_d["error"])
returnValue(them_d)
@inlineCallbacks
def handle_code(self, w):
@ -133,19 +128,65 @@ class TwistedReceiver:
self.msg(u"Verifier %s." % verifier_hex)
@inlineCallbacks
def get_data(self, w):
# this may raise WrongPasswordError
them_bytes = yield w.get()
them_d = json.loads(them_bytes.decode("utf-8"))
if "error" in them_d:
raise TransferError(them_d["error"])
returnValue(them_d)
def _parse_transit(self, sender_hints, w):
if self._transit_receiver:
# TODO: accept multiple messages, add the additional hints to the
# existing TransitReceiver
return
yield self._build_transit(w, sender_hints)
@inlineCallbacks
def _build_transit(self, w, sender_hints):
tr = TransitReceiver(self.args.transit_helper,
no_listen=self.args.no_listen,
tor_manager=self._tor_manager,
reactor=self._reactor,
timing=self.args.timing)
self._transit_receiver = tr
transit_key = w.derive_key(APPID+u"/transit-key", tr.TRANSIT_KEY_LENGTH)
tr.set_transit_key(transit_key)
tr.add_their_direct_hints(sender_hints["direct_connection_hints"])
tr.add_their_relay_hints(sender_hints["relay_connection_hints"])
direct_hints = yield tr.get_direct_hints()
relay_hints = yield tr.get_relay_hints()
receiver_hints = {
"direct_connection_hints": direct_hints,
"relay_connection_hints": relay_hints,
}
self._send_data({u"transit": receiver_hints}, w)
# TODO: send more hints as the TransitReceiver produces them
@inlineCallbacks
def parse_offer(self, them_d, w):
if "message" in them_d:
self.handle_text(them_d, w)
returnValue(None)
# transit will be created by this point, but not connected
if "file" in them_d:
f = self.handle_file(them_d)
self._send_permission(w)
rp = yield self._establish_transit()
yield self._transfer_data(rp, f)
self.write_file(f)
yield self.close_transit(rp)
elif "directory" in them_d:
f = self.handle_directory(them_d)
self._send_permission(w)
rp = yield self._establish_transit()
yield self._transfer_data(rp, f)
self.write_directory(f)
yield self.close_transit(rp)
else:
self.msg(u"I don't know what they're offering\n")
self.msg(u"Offer details: %r" % (them_d,))
raise RespondError("unknown offer type")
def handle_text(self, them_d, w):
# we're receiving a text message
self.msg(them_d["message"])
data = json.dumps({"answer": {"message_ack": "ok"}}).encode("utf-8")
w.send(data)
self._send_data({"answer": {"message_ack": "ok"}}, w)
def handle_file(self, them_d):
file_data = them_d["file"]
@ -202,39 +243,18 @@ class TwistedReceiver:
raise RespondError("transfer rejected")
t.detail(answer="yes")
@inlineCallbacks
def establish_transit(self, w, them_d):
transit_receiver = TransitReceiver(self.args.transit_helper,
no_listen=self.args.no_listen,
tor_manager=self._tor_manager,
reactor=self._reactor,
timing=self.args.timing)
transit_key = w.derive_key(APPID+u"/transit-key",
transit_receiver.TRANSIT_KEY_LENGTH)
transit_receiver.set_transit_key(transit_key)
direct_hints = yield transit_receiver.get_direct_hints()
relay_hints = yield transit_receiver.get_relay_hints()
data = json.dumps({
"answer": {
"file_ack": "ok",
"transit": {
"direct_connection_hints": direct_hints,
"relay_connection_hints": relay_hints,
},
},
}).encode("utf-8")
w.send(data)
def _send_permission(self, w):
self._send_data({"answer": { "file_ack": "ok" }}, w)
# now receive the rest of the owl
tdata = them_d["transit"]
transit_receiver.add_their_direct_hints(tdata["direct_connection_hints"])
transit_receiver.add_their_relay_hints(tdata["relay_connection_hints"])
record_pipe = yield transit_receiver.connect()
@inlineCallbacks
def _establish_transit(self):
record_pipe = yield self._transit_receiver.connect()
self.args.timing.add("transit connected")
returnValue(record_pipe)
@inlineCallbacks
def transfer_data(self, record_pipe, f):
def _transfer_data(self, record_pipe, f):
# now receive the rest of the owl
self.msg(u"Receiving (%s).." % record_pipe.describe())
with self.args.timing.add("rx file"):

View File

@ -51,6 +51,10 @@ class Sender:
d.addBoth(w.close)
yield d
def _send_data(self, data, w):
data_bytes = json.dumps(data).encode("utf-8")
w.send(data_bytes)
@inlineCallbacks
def _go(self, w):
# TODO: run the blocking zip-the-directory IO in a thread, let the
@ -100,18 +104,20 @@ class Sender:
reactor=self._reactor,
timing=self._timing)
self._transit_sender = ts
offer["transit"] = transit_data = {}
transit_data["relay_connection_hints"] = ts.get_relay_hints()
# for now, send this before the main offer
direct_hints = yield ts.get_direct_hints()
transit_data["direct_connection_hints"] = direct_hints
sender_hints = {"relay_connection_hints": ts.get_relay_hints(),
"direct_connection_hints": direct_hints,
}
self._send_data({u"transit": sender_hints}, w)
# TODO: move this down below w.get()
transit_key = w.derive_key(APPID+"/transit-key",
ts.TRANSIT_KEY_LENGTH)
ts.set_transit_key(transit_key)
my_offer_bytes = json.dumps({"offer": offer}).encode("utf-8")
w.send(my_offer_bytes)
self._send_data({"offer": offer}, w)
want_answer = True
done = False
@ -125,15 +131,23 @@ class Sender:
raise TransferError("unexpected close")
# TODO: get() fired, so now it's safe to use w.derive_key()
them_d = json.loads(them_d_bytes.decode("utf-8"))
#print("GOT", them_d)
if u"transit" in them_d:
yield self._handle_transit(them_d[u"transit"])
continue
if u"answer" in them_d:
if not want_answer:
raise TransferError("duplicate answer")
them_answer = them_d[u"answer"]
yield self._handle_answer(them_answer)
yield self._handle_answer(them_d[u"answer"])
done = True
returnValue(None)
log.msg("unrecognized message %r" % (them_d,))
def _handle_transit(self, receiver_hints):
ts = self._transit_sender
ts.add_their_direct_hints(receiver_hints["direct_connection_hints"])
ts.add_their_relay_hints(receiver_hints["relay_connection_hints"])
def _build_offer(self):
offer = {}
@ -223,15 +237,12 @@ class Sender:
raise TransferError("ambiguous response from remote, "
"transfer abandoned: %s" % (them_answer,))
tdata = them_answer["transit"]
yield self._send_file_twisted(tdata)
yield self._send_file_twisted()
@inlineCallbacks
def _send_file_twisted(self, tdata):
def _send_file_twisted(self):
ts = self._transit_sender
ts.add_their_direct_hints(tdata["direct_connection_hints"])
ts.add_their_relay_hints(tdata["relay_connection_hints"])
self._fd_to_send.seek(0,2)
filesize = self._fd_to_send.tell()