blob: 387ec4b9fc1d824d263a5f1a7e9fcdbe7e8a5754 [file] [log] [blame]
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000010#include <algorithm>
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000011
12#include "webrtc/p2p/base/relayport.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020013#include "webrtc/rtc_base/asyncpacketsocket.h"
14#include "webrtc/rtc_base/checks.h"
15#include "webrtc/rtc_base/helpers.h"
16#include "webrtc/rtc_base/logging.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000017
18namespace cricket {
19
honghaiz34b11eb2016-03-16 08:55:44 -070020static const int kMessageConnectTimeout = 1;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000021static const int kKeepAliveDelay = 10 * 60 * 1000;
22static const int kRetryTimeout = 50 * 1000; // ICE says 50 secs
23// How long to wait for a socket to connect to remote host in milliseconds
24// before trying another connection.
25static const int kSoftConnectTimeoutMs = 3 * 1000;
26
27// Handles a connection to one address/port/protocol combination for a
28// particular RelayEntry.
29class RelayConnection : public sigslot::has_slots<> {
30 public:
31 RelayConnection(const ProtocolAddress* protocol_address,
32 rtc::AsyncPacketSocket* socket,
33 rtc::Thread* thread);
34 ~RelayConnection();
35 rtc::AsyncPacketSocket* socket() const { return socket_; }
36
37 const ProtocolAddress* protocol_address() {
38 return protocol_address_;
39 }
40
41 rtc::SocketAddress GetAddress() const {
42 return protocol_address_->address;
43 }
44
45 ProtocolType GetProtocol() const {
46 return protocol_address_->proto;
47 }
48
49 int SetSocketOption(rtc::Socket::Option opt, int value);
50
51 // Validates a response to a STUN allocate request.
52 bool CheckResponse(StunMessage* msg);
53
54 // Sends data to the relay server.
55 int Send(const void* pv, size_t cb, const rtc::PacketOptions& options);
56
57 // Sends a STUN allocate request message to the relay server.
58 void SendAllocateRequest(RelayEntry* entry, int delay);
59
60 // Return the latest error generated by the socket.
61 int GetError() { return socket_->GetError(); }
62
63 // Called on behalf of a StunRequest to write data to the socket. This is
64 // already STUN intended for the server, so no wrapping is necessary.
65 void OnSendPacket(const void* data, size_t size, StunRequest* req);
66
67 private:
68 rtc::AsyncPacketSocket* socket_;
69 const ProtocolAddress* protocol_address_;
70 StunRequestManager *request_manager_;
71};
72
73// Manages a number of connections to the relayserver, one for each
74// available protocol. We aim to use each connection for only a
75// specific destination address so that we can avoid wrapping every
76// packet in a STUN send / data indication.
77class RelayEntry : public rtc::MessageHandler,
78 public sigslot::has_slots<> {
79 public:
80 RelayEntry(RelayPort* port, const rtc::SocketAddress& ext_addr);
81 ~RelayEntry();
82
83 RelayPort* port() { return port_; }
84
85 const rtc::SocketAddress& address() const { return ext_addr_; }
86 void set_address(const rtc::SocketAddress& addr) { ext_addr_ = addr; }
87
88 bool connected() const { return connected_; }
89 bool locked() const { return locked_; }
90
91 // Returns the last error on the socket of this entry.
92 int GetError();
93
94 // Returns the most preferred connection of the given
95 // ones. Connections are rated based on protocol in the order of:
96 // UDP, TCP and SSLTCP, where UDP is the most preferred protocol
97 static RelayConnection* GetBestConnection(RelayConnection* conn1,
98 RelayConnection* conn2);
99
100 // Sends the STUN requests to the server to initiate this connection.
101 void Connect();
102
103 // Called when this entry becomes connected. The address given is the one
104 // exposed to the outside world on the relay server.
105 void OnConnect(const rtc::SocketAddress& mapped_addr,
106 RelayConnection* socket);
107
108 // Sends a packet to the given destination address using the socket of this
109 // entry. This will wrap the packet in STUN if necessary.
110 int SendTo(const void* data, size_t size,
111 const rtc::SocketAddress& addr,
112 const rtc::PacketOptions& options);
113
114 // Schedules a keep-alive allocate request.
115 void ScheduleKeepAlive();
116
117 void SetServerIndex(size_t sindex) { server_index_ = sindex; }
118
119 // Sets this option on the socket of each connection.
120 int SetSocketOption(rtc::Socket::Option opt, int value);
121
122 size_t ServerIndex() const { return server_index_; }
123
124 // Try a different server address
125 void HandleConnectFailure(rtc::AsyncPacketSocket* socket);
126
127 // Implementation of the MessageHandler Interface.
128 virtual void OnMessage(rtc::Message *pmsg);
129
130 private:
131 RelayPort* port_;
132 rtc::SocketAddress ext_addr_;
133 size_t server_index_;
134 bool connected_;
135 bool locked_;
136 RelayConnection* current_connection_;
137
138 // Called when a TCP connection is established or fails
139 void OnSocketConnect(rtc::AsyncPacketSocket* socket);
140 void OnSocketClose(rtc::AsyncPacketSocket* socket, int error);
141
142 // Called when a packet is received on this socket.
143 void OnReadPacket(
144 rtc::AsyncPacketSocket* socket,
145 const char* data, size_t size,
146 const rtc::SocketAddress& remote_addr,
147 const rtc::PacketTime& packet_time);
stefanc1aeaf02015-10-15 07:26:07 -0700148
149 void OnSentPacket(rtc::AsyncPacketSocket* socket,
150 const rtc::SentPacket& sent_packet);
151
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000152 // Called when the socket is currently able to send.
153 void OnReadyToSend(rtc::AsyncPacketSocket* socket);
154
155 // Sends the given data on the socket to the server with no wrapping. This
156 // returns the number of bytes written or -1 if an error occurred.
157 int SendPacket(const void* data, size_t size,
158 const rtc::PacketOptions& options);
159};
160
161// Handles an allocate request for a particular RelayEntry.
162class AllocateRequest : public StunRequest {
163 public:
164 AllocateRequest(RelayEntry* entry, RelayConnection* connection);
165 virtual ~AllocateRequest() {}
166
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700167 void Prepare(StunMessage* request) override;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000168
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700169 void OnSent() override;
170 int resend_delay() override;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000171
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700172 void OnResponse(StunMessage* response) override;
173 void OnErrorResponse(StunMessage* response) override;
174 void OnTimeout() override;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000175
176 private:
177 RelayEntry* entry_;
178 RelayConnection* connection_;
honghaiz34b11eb2016-03-16 08:55:44 -0700179 int64_t start_time_;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000180};
181
pkasting@chromium.org332331f2014-11-06 20:19:22 +0000182RelayPort::RelayPort(rtc::Thread* thread,
183 rtc::PacketSocketFactory* factory,
184 rtc::Network* network,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200185 uint16_t min_port,
186 uint16_t max_port,
pkasting@chromium.org332331f2014-11-06 20:19:22 +0000187 const std::string& username,
188 const std::string& password)
Peter Boström0c4e06b2015-10-07 12:23:21 +0200189 : Port(thread,
190 RELAY_PORT_TYPE,
191 factory,
192 network,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200193 min_port,
194 max_port,
195 username,
196 password),
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000197 ready_(false),
198 error_(0) {
199 entries_.push_back(
200 new RelayEntry(this, rtc::SocketAddress()));
201 // TODO: set local preference value for TCP based candidates.
202}
203
204RelayPort::~RelayPort() {
205 for (size_t i = 0; i < entries_.size(); ++i)
206 delete entries_[i];
207 thread()->Clear(this);
208}
209
210void RelayPort::AddServerAddress(const ProtocolAddress& addr) {
deadbeeff137e972017-03-23 15:45:49 -0700211 // Since HTTP proxies usually only allow 443,
212 // let's up the priority on PROTO_SSLTCP
213 if (addr.proto == PROTO_SSLTCP &&
214 (proxy().type == rtc::PROXY_HTTPS ||
215 proxy().type == rtc::PROXY_UNKNOWN)) {
216 server_addr_.push_front(addr);
217 } else {
218 server_addr_.push_back(addr);
219 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000220}
221
222void RelayPort::AddExternalAddress(const ProtocolAddress& addr) {
223 std::string proto_name = ProtoToString(addr.proto);
224 for (std::vector<ProtocolAddress>::iterator it = external_addr_.begin();
225 it != external_addr_.end(); ++it) {
226 if ((it->address == addr.address) && (it->proto == addr.proto)) {
227 LOG(INFO) << "Redundant relay address: " << proto_name
228 << " @ " << addr.address.ToSensitiveString();
229 return;
230 }
231 }
232 external_addr_.push_back(addr);
233}
234
235void RelayPort::SetReady() {
236 if (!ready_) {
237 std::vector<ProtocolAddress>::iterator iter;
238 for (iter = external_addr_.begin();
239 iter != external_addr_.end(); ++iter) {
240 std::string proto_name = ProtoToString(iter->proto);
241 // In case of Gturn, related address is set to null socket address.
242 // This is due to as mapped address stun attribute is used for allocated
243 // address.
Guo-wei Shieh3d564c12015-08-19 16:51:15 -0700244 AddAddress(iter->address, iter->address, rtc::SocketAddress(), proto_name,
hnsl277b2502016-12-13 05:17:23 -0800245 proto_name, "", RELAY_PORT_TYPE, ICE_TYPE_PREFERENCE_RELAY_UDP,
zhihuang26d99c22017-02-13 12:47:27 -0800246 0, "", false);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000247 }
248 ready_ = true;
249 SignalPortComplete(this);
250 }
251}
252
253const ProtocolAddress * RelayPort::ServerAddress(size_t index) const {
254 if (index < server_addr_.size())
255 return &server_addr_[index];
256 return NULL;
257}
258
259bool RelayPort::HasMagicCookie(const char* data, size_t size) {
260 if (size < 24 + sizeof(TURN_MAGIC_COOKIE_VALUE)) {
261 return false;
262 } else {
263 return memcmp(data + 24,
264 TURN_MAGIC_COOKIE_VALUE,
265 sizeof(TURN_MAGIC_COOKIE_VALUE)) == 0;
266 }
267}
268
269void RelayPort::PrepareAddress() {
270 // We initiate a connect on the first entry. If this completes, it will fill
271 // in the server address as the address of this port.
nisseede5da42017-01-12 05:15:36 -0800272 RTC_DCHECK(entries_.size() == 1);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000273 entries_[0]->Connect();
274 ready_ = false;
275}
276
277Connection* RelayPort::CreateConnection(const Candidate& address,
278 CandidateOrigin origin) {
279 // We only create conns to non-udp sockets if they are incoming on this port
280 if ((address.protocol() != UDP_PROTOCOL_NAME) &&
281 (origin != ORIGIN_THIS_PORT)) {
282 return 0;
283 }
284
285 // We don't support loopback on relays
286 if (address.type() == Type()) {
287 return 0;
288 }
289
290 if (!IsCompatibleAddress(address.address())) {
291 return 0;
292 }
293
294 size_t index = 0;
295 for (size_t i = 0; i < Candidates().size(); ++i) {
296 const Candidate& local = Candidates()[i];
297 if (local.protocol() == address.protocol()) {
298 index = i;
299 break;
300 }
301 }
302
303 Connection * conn = new ProxyConnection(this, index, address);
honghaiz36f50e82016-06-01 15:57:03 -0700304 AddOrReplaceConnection(conn);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000305 return conn;
306}
307
308int RelayPort::SendTo(const void* data, size_t size,
309 const rtc::SocketAddress& addr,
310 const rtc::PacketOptions& options,
311 bool payload) {
312 // Try to find an entry for this specific address. Note that the first entry
313 // created was not given an address initially, so it can be set to the first
314 // address that comes along.
315 RelayEntry* entry = 0;
316
317 for (size_t i = 0; i < entries_.size(); ++i) {
318 if (entries_[i]->address().IsNil() && payload) {
319 entry = entries_[i];
320 entry->set_address(addr);
321 break;
322 } else if (entries_[i]->address() == addr) {
323 entry = entries_[i];
324 break;
325 }
326 }
327
328 // If we did not find one, then we make a new one. This will not be useable
329 // until it becomes connected, however.
330 if (!entry && payload) {
331 entry = new RelayEntry(this, addr);
332 if (!entries_.empty()) {
333 entry->SetServerIndex(entries_[0]->ServerIndex());
334 }
335 entry->Connect();
336 entries_.push_back(entry);
337 }
338
339 // If the entry is connected, then we can send on it (though wrapping may
340 // still be necessary). Otherwise, we can't yet use this connection, so we
341 // default to the first one.
342 if (!entry || !entry->connected()) {
nisseede5da42017-01-12 05:15:36 -0800343 RTC_DCHECK(!entries_.empty());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000344 entry = entries_[0];
345 if (!entry->connected()) {
skvladc309e0e2016-07-28 17:15:20 -0700346 error_ = ENOTCONN;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000347 return SOCKET_ERROR;
348 }
349 }
350
351 // Send the actual contents to the server using the usual mechanism.
352 int sent = entry->SendTo(data, size, addr, options);
353 if (sent <= 0) {
nisseede5da42017-01-12 05:15:36 -0800354 RTC_DCHECK(sent < 0);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000355 error_ = entry->GetError();
356 return SOCKET_ERROR;
357 }
358 // The caller of the function is expecting the number of user data bytes,
359 // rather than the size of the packet.
360 return static_cast<int>(size);
361}
362
363int RelayPort::SetOption(rtc::Socket::Option opt, int value) {
364 int result = 0;
365 for (size_t i = 0; i < entries_.size(); ++i) {
366 if (entries_[i]->SetSocketOption(opt, value) < 0) {
367 result = -1;
368 error_ = entries_[i]->GetError();
369 }
370 }
371 options_.push_back(OptionValue(opt, value));
372 return result;
373}
374
375int RelayPort::GetOption(rtc::Socket::Option opt, int* value) {
376 std::vector<OptionValue>::iterator it;
377 for (it = options_.begin(); it < options_.end(); ++it) {
378 if (it->first == opt) {
379 *value = it->second;
380 return 0;
381 }
382 }
383 return SOCKET_ERROR;
384}
385
386int RelayPort::GetError() {
387 return error_;
388}
389
390void RelayPort::OnReadPacket(
391 const char* data, size_t size,
392 const rtc::SocketAddress& remote_addr,
393 ProtocolType proto,
394 const rtc::PacketTime& packet_time) {
395 if (Connection* conn = GetConnection(remote_addr)) {
396 conn->OnReadPacket(data, size, packet_time);
397 } else {
398 Port::OnReadPacket(data, size, remote_addr, proto);
399 }
400}
401
402RelayConnection::RelayConnection(const ProtocolAddress* protocol_address,
403 rtc::AsyncPacketSocket* socket,
404 rtc::Thread* thread)
405 : socket_(socket),
406 protocol_address_(protocol_address) {
407 request_manager_ = new StunRequestManager(thread);
408 request_manager_->SignalSendPacket.connect(this,
409 &RelayConnection::OnSendPacket);
410}
411
412RelayConnection::~RelayConnection() {
413 delete request_manager_;
414 delete socket_;
415}
416
417int RelayConnection::SetSocketOption(rtc::Socket::Option opt,
418 int value) {
419 if (socket_) {
420 return socket_->SetOption(opt, value);
421 }
422 return 0;
423}
424
425bool RelayConnection::CheckResponse(StunMessage* msg) {
426 return request_manager_->CheckResponse(msg);
427}
428
429void RelayConnection::OnSendPacket(const void* data, size_t size,
430 StunRequest* req) {
431 // TODO(mallinath) Find a way to get DSCP value from Port.
432 rtc::PacketOptions options; // Default dscp set to NO_CHANGE.
433 int sent = socket_->SendTo(data, size, GetAddress(), options);
434 if (sent <= 0) {
435 LOG(LS_VERBOSE) << "OnSendPacket: failed sending to " << GetAddress() <<
436 strerror(socket_->GetError());
nisseede5da42017-01-12 05:15:36 -0800437 RTC_DCHECK(sent < 0);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000438 }
439}
440
441int RelayConnection::Send(const void* pv, size_t cb,
442 const rtc::PacketOptions& options) {
443 return socket_->SendTo(pv, cb, GetAddress(), options);
444}
445
446void RelayConnection::SendAllocateRequest(RelayEntry* entry, int delay) {
447 request_manager_->SendDelayed(new AllocateRequest(entry, this), delay);
448}
449
450RelayEntry::RelayEntry(RelayPort* port,
451 const rtc::SocketAddress& ext_addr)
452 : port_(port), ext_addr_(ext_addr),
453 server_index_(0), connected_(false), locked_(false),
454 current_connection_(NULL) {
455}
456
457RelayEntry::~RelayEntry() {
458 // Remove all RelayConnections and dispose sockets.
459 delete current_connection_;
460 current_connection_ = NULL;
461}
462
463void RelayEntry::Connect() {
464 // If we're already connected, return.
465 if (connected_)
466 return;
467
468 // If we've exhausted all options, bail out.
469 const ProtocolAddress* ra = port()->ServerAddress(server_index_);
470 if (!ra) {
471 LOG(LS_WARNING) << "No more relay addresses left to try";
472 return;
473 }
474
475 // Remove any previous connection.
476 if (current_connection_) {
477 port()->thread()->Dispose(current_connection_);
478 current_connection_ = NULL;
479 }
480
481 // Try to set up our new socket.
482 LOG(LS_INFO) << "Connecting to relay via " << ProtoToString(ra->proto) <<
483 " @ " << ra->address.ToSensitiveString();
484
485 rtc::AsyncPacketSocket* socket = NULL;
486
487 if (ra->proto == PROTO_UDP) {
488 // UDP sockets are simple.
489 socket = port_->socket_factory()->CreateUdpSocket(
deadbeef5c3c1042017-08-04 15:01:57 -0700490 rtc::SocketAddress(port_->Network()->GetBestIP(), 0), port_->min_port(),
491 port_->max_port());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000492 } else if (ra->proto == PROTO_TCP || ra->proto == PROTO_SSLTCP) {
hnsl04833622017-01-09 08:35:45 -0800493 int opts = (ra->proto == PROTO_SSLTCP)
494 ? rtc::PacketSocketFactory::OPT_TLS_FAKE
495 : 0;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000496 socket = port_->socket_factory()->CreateClientTcpSocket(
deadbeef5c3c1042017-08-04 15:01:57 -0700497 rtc::SocketAddress(port_->Network()->GetBestIP(), 0), ra->address,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000498 port_->proxy(), port_->user_agent(), opts);
499 } else {
500 LOG(LS_WARNING) << "Unknown protocol (" << ra->proto << ")";
501 }
502
503 if (!socket) {
504 LOG(LS_WARNING) << "Socket creation failed";
505 }
506
507 // If we failed to get a socket, move on to the next protocol.
508 if (!socket) {
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700509 port()->thread()->Post(RTC_FROM_HERE, this, kMessageConnectTimeout);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000510 return;
511 }
512
513 // Otherwise, create the new connection and configure any socket options.
514 socket->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket);
stefanc1aeaf02015-10-15 07:26:07 -0700515 socket->SignalSentPacket.connect(this, &RelayEntry::OnSentPacket);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000516 socket->SignalReadyToSend.connect(this, &RelayEntry::OnReadyToSend);
517 current_connection_ = new RelayConnection(ra, socket, port()->thread());
518 for (size_t i = 0; i < port_->options().size(); ++i) {
519 current_connection_->SetSocketOption(port_->options()[i].first,
520 port_->options()[i].second);
521 }
522
523 // If we're trying UDP, start binding requests.
524 // If we're trying TCP, wait for connection with a fixed timeout.
525 if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) {
526 socket->SignalClose.connect(this, &RelayEntry::OnSocketClose);
527 socket->SignalConnect.connect(this, &RelayEntry::OnSocketConnect);
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700528 port()->thread()->PostDelayed(RTC_FROM_HERE, kSoftConnectTimeoutMs, this,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000529 kMessageConnectTimeout);
530 } else {
531 current_connection_->SendAllocateRequest(this, 0);
532 }
533}
534
535int RelayEntry::GetError() {
536 if (current_connection_ != NULL) {
537 return current_connection_->GetError();
538 }
539 return 0;
540}
541
542RelayConnection* RelayEntry::GetBestConnection(RelayConnection* conn1,
543 RelayConnection* conn2) {
544 return conn1->GetProtocol() <= conn2->GetProtocol() ? conn1 : conn2;
545}
546
547void RelayEntry::OnConnect(const rtc::SocketAddress& mapped_addr,
548 RelayConnection* connection) {
549 // We are connected, notify our parent.
550 ProtocolType proto = PROTO_UDP;
551 LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto)
552 << " @ " << mapped_addr.ToSensitiveString();
553 connected_ = true;
554
555 port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto));
556 port_->SetReady();
557}
558
559int RelayEntry::SendTo(const void* data, size_t size,
560 const rtc::SocketAddress& addr,
561 const rtc::PacketOptions& options) {
562 // If this connection is locked to the address given, then we can send the
563 // packet with no wrapper.
564 if (locked_ && (ext_addr_ == addr))
565 return SendPacket(data, size, options);
566
567 // Otherwise, we must wrap the given data in a STUN SEND request so that we
568 // can communicate the destination address to the server.
569 //
570 // Note that we do not use a StunRequest here. This is because there is
571 // likely no reason to resend this packet. If it is late, we just drop it.
572 // The next send to this address will try again.
573
574 RelayMessage request;
575 request.SetType(STUN_SEND_REQUEST);
576
zsteinf42cc9d2017-03-27 16:17:19 -0700577 auto magic_cookie_attr =
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000578 StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
579 magic_cookie_attr->CopyBytes(TURN_MAGIC_COOKIE_VALUE,
580 sizeof(TURN_MAGIC_COOKIE_VALUE));
zsteinf42cc9d2017-03-27 16:17:19 -0700581 request.AddAttribute(std::move(magic_cookie_attr));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000582
zsteinf42cc9d2017-03-27 16:17:19 -0700583 auto username_attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000584 username_attr->CopyBytes(port_->username_fragment().c_str(),
585 port_->username_fragment().size());
zsteinf42cc9d2017-03-27 16:17:19 -0700586 request.AddAttribute(std::move(username_attr));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000587
zsteinf42cc9d2017-03-27 16:17:19 -0700588 auto addr_attr = StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000589 addr_attr->SetIP(addr.ipaddr());
590 addr_attr->SetPort(addr.port());
zsteinf42cc9d2017-03-27 16:17:19 -0700591 request.AddAttribute(std::move(addr_attr));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000592
593 // Attempt to lock
594 if (ext_addr_ == addr) {
zsteinf42cc9d2017-03-27 16:17:19 -0700595 auto options_attr = StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000596 options_attr->SetValue(0x1);
zsteinf42cc9d2017-03-27 16:17:19 -0700597 request.AddAttribute(std::move(options_attr));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000598 }
599
zsteinf42cc9d2017-03-27 16:17:19 -0700600 auto data_attr = StunAttribute::CreateByteString(STUN_ATTR_DATA);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000601 data_attr->CopyBytes(data, size);
zsteinf42cc9d2017-03-27 16:17:19 -0700602 request.AddAttribute(std::move(data_attr));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000603
604 // TODO: compute the HMAC.
605
jbauchf1f87202016-03-30 06:43:37 -0700606 rtc::ByteBufferWriter buf;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000607 request.Write(&buf);
608
609 return SendPacket(buf.Data(), buf.Length(), options);
610}
611
612void RelayEntry::ScheduleKeepAlive() {
613 if (current_connection_) {
614 current_connection_->SendAllocateRequest(this, kKeepAliveDelay);
615 }
616}
617
618int RelayEntry::SetSocketOption(rtc::Socket::Option opt, int value) {
619 // Set the option on all available sockets.
620 int socket_error = 0;
621 if (current_connection_) {
622 socket_error = current_connection_->SetSocketOption(opt, value);
623 }
624 return socket_error;
625}
626
627void RelayEntry::HandleConnectFailure(
628 rtc::AsyncPacketSocket* socket) {
629 // Make sure it's the current connection that has failed, it might
630 // be an old socked that has not yet been disposed.
631 if (!socket ||
632 (current_connection_ && socket == current_connection_->socket())) {
633 if (current_connection_)
634 port()->SignalConnectFailure(current_connection_->protocol_address());
635
636 // Try to connect to the next server address.
637 server_index_ += 1;
638 Connect();
639 }
640}
641
642void RelayEntry::OnMessage(rtc::Message *pmsg) {
nisseede5da42017-01-12 05:15:36 -0800643 RTC_DCHECK(pmsg->message_id == kMessageConnectTimeout);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000644 if (current_connection_) {
645 const ProtocolAddress* ra = current_connection_->protocol_address();
646 LOG(LS_WARNING) << "Relay " << ra->proto << " connection to " <<
647 ra->address << " timed out";
648
649 // Currently we connect to each server address in sequence. If we
650 // have more addresses to try, treat this is an error and move on to
651 // the next address, otherwise give this connection more time and
652 // await the real timeout.
653 //
654 // TODO: Connect to servers in parallel to speed up connect time
655 // and to avoid giving up too early.
656 port_->SignalSoftTimeout(ra);
657 HandleConnectFailure(current_connection_->socket());
658 } else {
659 HandleConnectFailure(NULL);
660 }
661}
662
663void RelayEntry::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
664 LOG(INFO) << "relay tcp connected to " <<
665 socket->GetRemoteAddress().ToSensitiveString();
666 if (current_connection_ != NULL) {
667 current_connection_->SendAllocateRequest(this, 0);
668 }
669}
670
671void RelayEntry::OnSocketClose(rtc::AsyncPacketSocket* socket,
672 int error) {
673 PLOG(LERROR, error) << "Relay connection failed: socket closed";
674 HandleConnectFailure(socket);
675}
676
677void RelayEntry::OnReadPacket(
678 rtc::AsyncPacketSocket* socket,
679 const char* data, size_t size,
680 const rtc::SocketAddress& remote_addr,
681 const rtc::PacketTime& packet_time) {
nisseede5da42017-01-12 05:15:36 -0800682 // RTC_DCHECK(remote_addr == port_->server_addr());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000683 // TODO: are we worried about this?
684
685 if (current_connection_ == NULL || socket != current_connection_->socket()) {
686 // This packet comes from an unknown address.
687 LOG(WARNING) << "Dropping packet: unknown address";
688 return;
689 }
690
691 // If the magic cookie is not present, then this is an unwrapped packet sent
692 // by the server, The actual remote address is the one we recorded.
693 if (!port_->HasMagicCookie(data, size)) {
694 if (locked_) {
695 port_->OnReadPacket(data, size, ext_addr_, PROTO_UDP, packet_time);
696 } else {
697 LOG(WARNING) << "Dropping packet: entry not locked";
698 }
699 return;
700 }
701
jbauchf1f87202016-03-30 06:43:37 -0700702 rtc::ByteBufferReader buf(data, size);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000703 RelayMessage msg;
704 if (!msg.Read(&buf)) {
705 LOG(INFO) << "Incoming packet was not STUN";
706 return;
707 }
708
709 // The incoming packet should be a STUN ALLOCATE response, SEND response, or
710 // DATA indication.
711 if (current_connection_->CheckResponse(&msg)) {
712 return;
713 } else if (msg.type() == STUN_SEND_RESPONSE) {
714 if (const StunUInt32Attribute* options_attr =
715 msg.GetUInt32(STUN_ATTR_OPTIONS)) {
716 if (options_attr->value() & 0x1) {
717 locked_ = true;
718 }
719 }
720 return;
721 } else if (msg.type() != STUN_DATA_INDICATION) {
722 LOG(INFO) << "Received BAD stun type from server: " << msg.type();
723 return;
724 }
725
726 // This must be a data indication.
727
728 const StunAddressAttribute* addr_attr =
729 msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
730 if (!addr_attr) {
731 LOG(INFO) << "Data indication has no source address";
732 return;
733 } else if (addr_attr->family() != 1) {
734 LOG(INFO) << "Source address has bad family";
735 return;
736 }
737
738 rtc::SocketAddress remote_addr2(addr_attr->ipaddr(), addr_attr->port());
739
740 const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA);
741 if (!data_attr) {
742 LOG(INFO) << "Data indication has no data";
743 return;
744 }
745
746 // Process the actual data and remote address in the normal manner.
747 port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2,
748 PROTO_UDP, packet_time);
749}
750
stefanc1aeaf02015-10-15 07:26:07 -0700751void RelayEntry::OnSentPacket(rtc::AsyncPacketSocket* socket,
752 const rtc::SentPacket& sent_packet) {
Stefan Holmer55674ff2016-01-14 15:49:16 +0100753 port_->OnSentPacket(socket, sent_packet);
stefanc1aeaf02015-10-15 07:26:07 -0700754}
755
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000756void RelayEntry::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
757 if (connected()) {
758 port_->OnReadyToSend();
759 }
760}
761
762int RelayEntry::SendPacket(const void* data, size_t size,
763 const rtc::PacketOptions& options) {
764 int sent = 0;
765 if (current_connection_) {
766 // We are connected, no need to send packets anywere else than to
767 // the current connection.
768 sent = current_connection_->Send(data, size, options);
769 }
770 return sent;
771}
772
773AllocateRequest::AllocateRequest(RelayEntry* entry,
774 RelayConnection* connection)
775 : StunRequest(new RelayMessage()),
776 entry_(entry),
777 connection_(connection) {
nisse1bffc1d2016-05-02 08:18:55 -0700778 start_time_ = rtc::TimeMillis();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000779}
780
781void AllocateRequest::Prepare(StunMessage* request) {
782 request->SetType(STUN_ALLOCATE_REQUEST);
783
zsteinf42cc9d2017-03-27 16:17:19 -0700784 auto username_attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000785 username_attr->CopyBytes(
786 entry_->port()->username_fragment().c_str(),
787 entry_->port()->username_fragment().size());
zsteinf42cc9d2017-03-27 16:17:19 -0700788 request->AddAttribute(std::move(username_attr));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000789}
790
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700791void AllocateRequest::OnSent() {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000792 count_ += 1;
793 if (count_ == 5)
794 timeout_ = true;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000795}
796
Peter Thatcher1cf6f812015-05-15 10:40:45 -0700797int AllocateRequest::resend_delay() {
798 if (count_ == 0) {
799 return 0;
800 }
801 return 100 * std::max(1 << (count_-1), 2);
802}
803
804
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000805void AllocateRequest::OnResponse(StunMessage* response) {
806 const StunAddressAttribute* addr_attr =
807 response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
808 if (!addr_attr) {
809 LOG(INFO) << "Allocate response missing mapped address.";
810 } else if (addr_attr->family() != 1) {
811 LOG(INFO) << "Mapped address has bad family";
812 } else {
813 rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
814 entry_->OnConnect(addr, connection_);
815 }
816
817 // We will do a keep-alive regardless of whether this request suceeds.
818 // This should have almost no impact on network usage.
819 entry_->ScheduleKeepAlive();
820}
821
822void AllocateRequest::OnErrorResponse(StunMessage* response) {
823 const StunErrorCodeAttribute* attr = response->GetErrorCode();
824 if (!attr) {
deadbeef996fc6b2017-04-26 09:21:22 -0700825 LOG(LS_ERROR) << "Missing allocate response error code.";
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000826 } else {
827 LOG(INFO) << "Allocate error response:"
828 << " code=" << attr->code()
829 << " reason='" << attr->reason() << "'";
830 }
831
nisse1bffc1d2016-05-02 08:18:55 -0700832 if (rtc::TimeMillis() - start_time_ <= kRetryTimeout)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000833 entry_->ScheduleKeepAlive();
834}
835
836void AllocateRequest::OnTimeout() {
837 LOG(INFO) << "Allocate request timed out";
838 entry_->HandleConnectFailure(connection_->socket());
839}
840
841} // namespace cricket