blob: f90c3dac0d555f173e692ea1302b4e1d9b84c23f [file] [log] [blame]
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +00001/*
2 * Copyright 2012 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 */
10
Steve Anton10542f22019-01-11 09:11:00 -080011#ifndef P2P_BASE_TURN_SERVER_H_
12#define P2P_BASE_TURN_SERVER_H_
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000013
14#include <list>
15#include <map>
kwiberg3ec46792016-04-27 07:22:53 -070016#include <memory>
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000017#include <set>
18#include <string>
Steve Anton6c38cc72017-11-29 10:25:58 -080019#include <utility>
deadbeef824f5862016-08-24 15:06:53 -070020#include <vector>
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000021
Artem Titovd15a5752021-02-10 14:31:24 +010022#include "api/sequence_checker.h"
Steve Anton10542f22019-01-11 09:11:00 -080023#include "p2p/base/port_interface.h"
Steve Anton10542f22019-01-11 09:11:00 -080024#include "rtc_base/async_packet_socket.h"
Steve Anton10542f22019-01-11 09:11:00 -080025#include "rtc_base/socket_address.h"
Artem Titove41c4332018-07-25 15:04:28 +020026#include "rtc_base/third_party/sigslot/sigslot.h"
Sebastian Jansson4db28b52020-01-08 14:07:15 +010027#include "rtc_base/thread.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000028
29namespace rtc {
jbauchf1f87202016-03-30 06:43:37 -070030class ByteBufferWriter;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000031class PacketSocketFactory;
Jonas Olssona4d87372019-07-05 19:08:33 +020032} // namespace rtc
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000033
34namespace cricket {
35
36class StunMessage;
37class TurnMessage;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +000038class TurnServer;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000039
40// The default server port for TURN, as specified in RFC5766.
41const int TURN_SERVER_PORT = 3478;
42
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +000043// Encapsulates the client's connection to the server.
44class TurnServerConnection {
45 public:
46 TurnServerConnection() : proto_(PROTO_UDP), socket_(NULL) {}
47 TurnServerConnection(const rtc::SocketAddress& src,
48 ProtocolType proto,
49 rtc::AsyncPacketSocket* socket);
50 const rtc::SocketAddress& src() const { return src_; }
51 rtc::AsyncPacketSocket* socket() { return socket_; }
52 bool operator==(const TurnServerConnection& t) const;
53 bool operator<(const TurnServerConnection& t) const;
54 std::string ToString() const;
55
56 private:
57 rtc::SocketAddress src_;
58 rtc::SocketAddress dst_;
59 cricket::ProtocolType proto_;
60 rtc::AsyncPacketSocket* socket_;
61};
62
63// Encapsulates a TURN allocation.
64// The object is created when an allocation request is received, and then
65// handles TURN messages (via HandleTurnMessage) and channel data messages
66// (via HandleChannelData) for this allocation when received by the server.
67// The object self-deletes and informs the server if its lifetime timer expires.
Tomas Gunnarssonabdb4702020-09-05 18:43:36 +020068class TurnServerAllocation : public rtc::MessageHandlerAutoCleanup,
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +000069 public sigslot::has_slots<> {
70 public:
71 TurnServerAllocation(TurnServer* server_,
72 rtc::Thread* thread,
73 const TurnServerConnection& conn,
74 rtc::AsyncPacketSocket* server_socket,
75 const std::string& key);
Steve Antonf2737d22017-10-31 16:27:34 -070076 ~TurnServerAllocation() override;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +000077
78 TurnServerConnection* conn() { return &conn_; }
79 const std::string& key() const { return key_; }
80 const std::string& transaction_id() const { return transaction_id_; }
81 const std::string& username() const { return username_; }
82 const std::string& origin() const { return origin_; }
83 const std::string& last_nonce() const { return last_nonce_; }
84 void set_last_nonce(const std::string& nonce) { last_nonce_ = nonce; }
85
86 std::string ToString() const;
87
88 void HandleTurnMessage(const TurnMessage* msg);
89 void HandleChannelData(const char* data, size_t size);
90
91 sigslot::signal1<TurnServerAllocation*> SignalDestroyed;
92
93 private:
94 class Channel;
95 class Permission;
96 typedef std::list<Permission*> PermissionList;
97 typedef std::list<Channel*> ChannelList;
98
99 void HandleAllocateRequest(const TurnMessage* msg);
100 void HandleRefreshRequest(const TurnMessage* msg);
101 void HandleSendIndication(const TurnMessage* msg);
102 void HandleCreatePermissionRequest(const TurnMessage* msg);
103 void HandleChannelBindRequest(const TurnMessage* msg);
104
105 void OnExternalPacket(rtc::AsyncPacketSocket* socket,
Niels Möllere6933812018-11-05 13:01:41 +0100106 const char* data,
107 size_t size,
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000108 const rtc::SocketAddress& addr,
Niels Möllere6933812018-11-05 13:01:41 +0100109 const int64_t& packet_time_us);
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000110
111 static int ComputeLifetime(const TurnMessage* msg);
112 bool HasPermission(const rtc::IPAddress& addr);
113 void AddPermission(const rtc::IPAddress& addr);
114 Permission* FindPermission(const rtc::IPAddress& addr) const;
115 Channel* FindChannel(int channel_id) const;
116 Channel* FindChannel(const rtc::SocketAddress& addr) const;
117
118 void SendResponse(TurnMessage* msg);
119 void SendBadRequestResponse(const TurnMessage* req);
Jonas Olssona4d87372019-07-05 19:08:33 +0200120 void SendErrorResponse(const TurnMessage* req,
121 int code,
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000122 const std::string& reason);
Jonas Olssona4d87372019-07-05 19:08:33 +0200123 void SendExternal(const void* data,
124 size_t size,
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000125 const rtc::SocketAddress& peer);
126
127 void OnPermissionDestroyed(Permission* perm);
128 void OnChannelDestroyed(Channel* channel);
Steve Antonf2737d22017-10-31 16:27:34 -0700129 void OnMessage(rtc::Message* msg) override;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000130
Niels Möller76b51e22021-03-18 15:44:24 +0100131 TurnServer* const server_;
132 rtc::Thread* const thread_;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000133 TurnServerConnection conn_;
kwiberg3ec46792016-04-27 07:22:53 -0700134 std::unique_ptr<rtc::AsyncPacketSocket> external_socket_;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000135 std::string key_;
136 std::string transaction_id_;
137 std::string username_;
138 std::string origin_;
139 std::string last_nonce_;
140 PermissionList perms_;
141 ChannelList channels_;
142};
143
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000144// An interface through which the MD5 credential hash can be retrieved.
145class TurnAuthInterface {
146 public:
147 // Gets HA1 for the specified user and realm.
148 // HA1 = MD5(A1) = MD5(username:realm:password).
149 // Return true if the given username and realm are valid, or false if not.
Jonas Olssona4d87372019-07-05 19:08:33 +0200150 virtual bool GetKey(const std::string& username,
151 const std::string& realm,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000152 std::string* key) = 0;
Henrik Kjellander3fe372d2016-05-12 08:10:52 +0200153 virtual ~TurnAuthInterface() = default;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000154};
155
156// An interface enables Turn Server to control redirection behavior.
157class TurnRedirectInterface {
158 public:
159 virtual bool ShouldRedirect(const rtc::SocketAddress& address,
160 rtc::SocketAddress* out) = 0;
161 virtual ~TurnRedirectInterface() {}
162};
163
Jonas Orelandbdcee282017-10-10 14:01:40 +0200164class StunMessageObserver {
165 public:
166 virtual void ReceivedMessage(const TurnMessage* msg) = 0;
167 virtual void ReceivedChannelData(const char* data, size_t size) = 0;
168 virtual ~StunMessageObserver() {}
169};
170
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000171// The core TURN server class. Give it a socket to listen on via
172// AddInternalServerSocket, and a factory to create external sockets via
173// SetExternalSocketFactory, and it's ready to go.
174// Not yet wired up: TCP support.
175class TurnServer : public sigslot::has_slots<> {
176 public:
deadbeef97943662016-07-12 11:04:50 -0700177 typedef std::map<TurnServerConnection, std::unique_ptr<TurnServerAllocation>>
178 AllocationMap;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000179
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000180 explicit TurnServer(rtc::Thread* thread);
Steve Antonf2737d22017-10-31 16:27:34 -0700181 ~TurnServer() override;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000182
183 // Gets/sets the realm value to use for the server.
Seth Hampsonaed71642018-06-11 07:41:32 -0700184 const std::string& realm() const {
Niels Möller76b51e22021-03-18 15:44:24 +0100185 RTC_DCHECK_RUN_ON(thread_);
Seth Hampsonaed71642018-06-11 07:41:32 -0700186 return realm_;
187 }
188 void set_realm(const std::string& realm) {
Niels Möller76b51e22021-03-18 15:44:24 +0100189 RTC_DCHECK_RUN_ON(thread_);
Seth Hampsonaed71642018-06-11 07:41:32 -0700190 realm_ = realm;
191 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000192
193 // Gets/sets the value for the SOFTWARE attribute for TURN messages.
Seth Hampsonaed71642018-06-11 07:41:32 -0700194 const std::string& software() const {
Niels Möller76b51e22021-03-18 15:44:24 +0100195 RTC_DCHECK_RUN_ON(thread_);
Seth Hampsonaed71642018-06-11 07:41:32 -0700196 return software_;
197 }
198 void set_software(const std::string& software) {
Niels Möller76b51e22021-03-18 15:44:24 +0100199 RTC_DCHECK_RUN_ON(thread_);
Seth Hampsonaed71642018-06-11 07:41:32 -0700200 software_ = software;
201 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000202
Seth Hampsonaed71642018-06-11 07:41:32 -0700203 const AllocationMap& allocations() const {
Niels Möller76b51e22021-03-18 15:44:24 +0100204 RTC_DCHECK_RUN_ON(thread_);
Seth Hampsonaed71642018-06-11 07:41:32 -0700205 return allocations_;
206 }
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000207
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000208 // Sets the authentication callback; does not take ownership.
Seth Hampsonaed71642018-06-11 07:41:32 -0700209 void set_auth_hook(TurnAuthInterface* auth_hook) {
Niels Möller76b51e22021-03-18 15:44:24 +0100210 RTC_DCHECK_RUN_ON(thread_);
Seth Hampsonaed71642018-06-11 07:41:32 -0700211 auth_hook_ = auth_hook;
212 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000213
214 void set_redirect_hook(TurnRedirectInterface* redirect_hook) {
Niels Möller76b51e22021-03-18 15:44:24 +0100215 RTC_DCHECK_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000216 redirect_hook_ = redirect_hook;
217 }
218
Seth Hampsonaed71642018-06-11 07:41:32 -0700219 void set_enable_otu_nonce(bool enable) {
Niels Möller76b51e22021-03-18 15:44:24 +0100220 RTC_DCHECK_RUN_ON(thread_);
Seth Hampsonaed71642018-06-11 07:41:32 -0700221 enable_otu_nonce_ = enable;
222 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000223
deadbeef376e1232015-11-25 09:00:08 -0800224 // If set to true, reject CreatePermission requests to RFC1918 addresses.
225 void set_reject_private_addresses(bool filter) {
Niels Möller76b51e22021-03-18 15:44:24 +0100226 RTC_DCHECK_RUN_ON(thread_);
deadbeef376e1232015-11-25 09:00:08 -0800227 reject_private_addresses_ = filter;
228 }
229
Taylor Brandstetteref184702016-06-23 17:35:47 -0700230 void set_enable_permission_checks(bool enable) {
Niels Möller76b51e22021-03-18 15:44:24 +0100231 RTC_DCHECK_RUN_ON(thread_);
Taylor Brandstetteref184702016-06-23 17:35:47 -0700232 enable_permission_checks_ = enable;
233 }
234
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000235 // Starts listening for packets from internal clients.
Jonas Olssona4d87372019-07-05 19:08:33 +0200236 void AddInternalSocket(rtc::AsyncPacketSocket* socket, ProtocolType proto);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000237 // Starts listening for the connections on this socket. When someone tries
238 // to connect, the connection will be accepted and a new internal socket
239 // will be added.
Jonas Olssona4d87372019-07-05 19:08:33 +0200240 void AddInternalServerSocket(rtc::AsyncSocket* socket, ProtocolType proto);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000241 // Specifies the factory to use for creating external sockets.
242 void SetExternalSocketFactory(rtc::PacketSocketFactory* factory,
243 const rtc::SocketAddress& address);
honghaizc463e202016-02-01 15:19:08 -0800244 // For testing only.
honghaiz34b11eb2016-03-16 08:55:44 -0700245 std::string SetTimestampForNextNonce(int64_t timestamp) {
Niels Möller76b51e22021-03-18 15:44:24 +0100246 RTC_DCHECK_RUN_ON(thread_);
honghaizc463e202016-02-01 15:19:08 -0800247 ts_for_next_nonce_ = timestamp;
248 return GenerateNonce(timestamp);
249 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000250
Jonas Olssona4d87372019-07-05 19:08:33 +0200251 void SetStunMessageObserver(std::unique_ptr<StunMessageObserver> observer) {
Niels Möller76b51e22021-03-18 15:44:24 +0100252 RTC_DCHECK_RUN_ON(thread_);
Jonas Orelandbdcee282017-10-10 14:01:40 +0200253 stun_message_observer_ = std::move(observer);
254 }
255
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000256 private:
Niels Möller76b51e22021-03-18 15:44:24 +0100257 // All private member functions and variables should have access restricted to
258 // thread_. But compile-time annotations are missing for members access from
259 // TurnServerAllocation (via friend declaration), and the On* methods, which
260 // are called via sigslot.
261 std::string GenerateNonce(int64_t now) const RTC_RUN_ON(thread_);
Niels Möllere6933812018-11-05 13:01:41 +0100262 void OnInternalPacket(rtc::AsyncPacketSocket* socket,
263 const char* data,
264 size_t size,
265 const rtc::SocketAddress& address,
266 const int64_t& packet_time_us);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000267
268 void OnNewInternalConnection(rtc::AsyncSocket* socket);
269
270 // Accept connections on this server socket.
Niels Möller76b51e22021-03-18 15:44:24 +0100271 void AcceptConnection(rtc::AsyncSocket* server_socket) RTC_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000272 void OnInternalSocketClose(rtc::AsyncPacketSocket* socket, int err);
273
Jonas Olssona4d87372019-07-05 19:08:33 +0200274 void HandleStunMessage(TurnServerConnection* conn,
275 const char* data,
Niels Möller76b51e22021-03-18 15:44:24 +0100276 size_t size) RTC_RUN_ON(thread_);
277 void HandleBindingRequest(TurnServerConnection* conn, const StunMessage* msg)
278 RTC_RUN_ON(thread_);
Jonas Olssona4d87372019-07-05 19:08:33 +0200279 void HandleAllocateRequest(TurnServerConnection* conn,
280 const TurnMessage* msg,
Niels Möller76b51e22021-03-18 15:44:24 +0100281 const std::string& key) RTC_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000282
Niels Möller76b51e22021-03-18 15:44:24 +0100283 bool GetKey(const StunMessage* msg, std::string* key) RTC_RUN_ON(thread_);
Jonas Olssona4d87372019-07-05 19:08:33 +0200284 bool CheckAuthorization(TurnServerConnection* conn,
Harald Alvestrand07d83c82021-03-02 08:09:53 +0000285 StunMessage* msg,
Jonas Olssona4d87372019-07-05 19:08:33 +0200286 const char* data,
287 size_t size,
Niels Möller76b51e22021-03-18 15:44:24 +0100288 const std::string& key) RTC_RUN_ON(thread_);
289 bool ValidateNonce(const std::string& nonce) const RTC_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000290
Niels Möller76b51e22021-03-18 15:44:24 +0100291 TurnServerAllocation* FindAllocation(TurnServerConnection* conn)
292 RTC_RUN_ON(thread_);
Jonas Olssona4d87372019-07-05 19:08:33 +0200293 TurnServerAllocation* CreateAllocation(TurnServerConnection* conn,
294 int proto,
Niels Möller76b51e22021-03-18 15:44:24 +0100295 const std::string& key)
296 RTC_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000297
Jonas Olssona4d87372019-07-05 19:08:33 +0200298 void SendErrorResponse(TurnServerConnection* conn,
299 const StunMessage* req,
300 int code,
301 const std::string& reason);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000302
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000303 void SendErrorResponseWithRealmAndNonce(TurnServerConnection* conn,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000304 const StunMessage* req,
305 int code,
Niels Möller76b51e22021-03-18 15:44:24 +0100306 const std::string& reason)
307 RTC_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000308
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000309 void SendErrorResponseWithAlternateServer(TurnServerConnection* conn,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000310 const StunMessage* req,
Niels Möller76b51e22021-03-18 15:44:24 +0100311 const rtc::SocketAddress& addr)
312 RTC_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000313
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000314 void SendStun(TurnServerConnection* conn, StunMessage* msg);
jbauchf1f87202016-03-30 06:43:37 -0700315 void Send(TurnServerConnection* conn, const rtc::ByteBufferWriter& buf);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000316
Niels Möller76b51e22021-03-18 15:44:24 +0100317 void OnAllocationDestroyed(TurnServerAllocation* allocation)
318 RTC_RUN_ON(thread_);
319 void DestroyInternalSocket(rtc::AsyncPacketSocket* socket)
320 RTC_RUN_ON(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000321
Jonas Olssona4d87372019-07-05 19:08:33 +0200322 typedef std::map<rtc::AsyncPacketSocket*, ProtocolType> InternalSocketMap;
323 typedef std::map<rtc::AsyncSocket*, ProtocolType> ServerSocketMap;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000324
Niels Möller76b51e22021-03-18 15:44:24 +0100325 rtc::Thread* const thread_;
326 const std::string nonce_key_;
327 std::string realm_ RTC_GUARDED_BY(thread_);
328 std::string software_ RTC_GUARDED_BY(thread_);
329 TurnAuthInterface* auth_hook_ RTC_GUARDED_BY(thread_);
330 TurnRedirectInterface* redirect_hook_ RTC_GUARDED_BY(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000331 // otu - one-time-use. Server will respond with 438 if it's
332 // sees the same nonce in next transaction.
Niels Möller76b51e22021-03-18 15:44:24 +0100333 bool enable_otu_nonce_ RTC_GUARDED_BY(thread_);
deadbeef376e1232015-11-25 09:00:08 -0800334 bool reject_private_addresses_ = false;
Taylor Brandstetteref184702016-06-23 17:35:47 -0700335 // Check for permission when receiving an external packet.
336 bool enable_permission_checks_ = true;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000337
Niels Möller76b51e22021-03-18 15:44:24 +0100338 InternalSocketMap server_sockets_ RTC_GUARDED_BY(thread_);
339 ServerSocketMap server_listen_sockets_ RTC_GUARDED_BY(thread_);
Niels Möller76b51e22021-03-18 15:44:24 +0100340 std::unique_ptr<rtc::PacketSocketFactory> external_socket_factory_
341 RTC_GUARDED_BY(thread_);
342 rtc::SocketAddress external_addr_ RTC_GUARDED_BY(thread_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000343
Niels Möller76b51e22021-03-18 15:44:24 +0100344 AllocationMap allocations_ RTC_GUARDED_BY(thread_);
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000345
honghaizc463e202016-02-01 15:19:08 -0800346 // For testing only. If this is non-zero, the next NONCE will be generated
347 // from this value, and it will be reset to 0 after generating the NONCE.
Niels Möller76b51e22021-03-18 15:44:24 +0100348 int64_t ts_for_next_nonce_ RTC_GUARDED_BY(thread_) = 0;
honghaizc463e202016-02-01 15:19:08 -0800349
Jonas Orelandbdcee282017-10-10 14:01:40 +0200350 // For testing only. Used to observe STUN messages received.
Niels Möller76b51e22021-03-18 15:44:24 +0100351 std::unique_ptr<StunMessageObserver> stun_message_observer_
352 RTC_GUARDED_BY(thread_);
Jonas Orelandbdcee282017-10-10 14:01:40 +0200353
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000354 friend class TurnServerAllocation;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000355};
356
357} // namespace cricket
358
Steve Anton10542f22019-01-11 09:11:00 -0800359#endif // P2P_BASE_TURN_SERVER_H_