OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2004--2008, Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 #include "pseudotcpchannel.h" | |
29 #include "webrtc/p2p/base/constants.h" | |
30 #include "webrtc/p2p/base/transportchannel.h" | |
31 #include "webrtc/libjingle/xmllite/xmlelement.h" | |
32 #include "tunnelsessionclient.h" | |
33 #include "webrtc/base/basicdefs.h" | |
34 #include "webrtc/base/basictypes.h" | |
35 #include "webrtc/base/common.h" | |
36 #include "webrtc/base/helpers.h" | |
37 #include "webrtc/base/logging.h" | |
38 #include "webrtc/base/stringutils.h" | |
39 | |
40 namespace cricket { | |
41 | |
42 const char NS_TUNNEL[] = "http://www.google.com/talk/tunnel"; | |
43 const buzz::StaticQName QN_TUNNEL_DESCRIPTION = { NS_TUNNEL, "description" }; | |
44 const buzz::StaticQName QN_TUNNEL_TYPE = { NS_TUNNEL, "type" }; | |
45 const char CN_TUNNEL[] = "tunnel"; | |
46 | |
47 enum { | |
48 MSG_CLOCK = 1, | |
49 MSG_DESTROY, | |
50 MSG_TERMINATE, | |
51 MSG_EVENT, | |
52 MSG_CREATE_TUNNEL, | |
53 }; | |
54 | |
55 struct EventData : public rtc::MessageData { | |
56 int event, error; | |
57 EventData(int ev, int err = 0) : event(ev), error(err) { } | |
58 }; | |
59 | |
60 struct CreateTunnelData : public rtc::MessageData { | |
61 buzz::Jid jid; | |
62 std::string description; | |
63 rtc::Thread* thread; | |
64 rtc::StreamInterface* stream; | |
65 }; | |
66 | |
67 extern const rtc::ConstantLabel SESSION_STATES[]; | |
68 | |
69 const rtc::ConstantLabel SESSION_STATES[] = { | |
70 KLABEL(Session::STATE_INIT), | |
71 KLABEL(Session::STATE_SENTINITIATE), | |
72 KLABEL(Session::STATE_RECEIVEDINITIATE), | |
73 KLABEL(Session::STATE_SENTACCEPT), | |
74 KLABEL(Session::STATE_RECEIVEDACCEPT), | |
75 KLABEL(Session::STATE_SENTMODIFY), | |
76 KLABEL(Session::STATE_RECEIVEDMODIFY), | |
77 KLABEL(Session::STATE_SENTREJECT), | |
78 KLABEL(Session::STATE_RECEIVEDREJECT), | |
79 KLABEL(Session::STATE_SENTREDIRECT), | |
80 KLABEL(Session::STATE_SENTTERMINATE), | |
81 KLABEL(Session::STATE_RECEIVEDTERMINATE), | |
82 KLABEL(Session::STATE_INPROGRESS), | |
83 KLABEL(Session::STATE_DEINIT), | |
84 LASTLABEL | |
85 }; | |
86 | |
87 /////////////////////////////////////////////////////////////////////////////// | |
88 // TunnelContentDescription | |
89 /////////////////////////////////////////////////////////////////////////////// | |
90 | |
91 struct TunnelContentDescription : public ContentDescription { | |
92 std::string description; | |
93 | |
94 TunnelContentDescription(const std::string& desc) : description(desc) { } | |
95 virtual ContentDescription* Copy() const { | |
96 return new TunnelContentDescription(*this); | |
97 } | |
98 }; | |
99 | |
100 /////////////////////////////////////////////////////////////////////////////// | |
101 // TunnelSessionClientBase | |
102 /////////////////////////////////////////////////////////////////////////////// | |
103 | |
104 TunnelSessionClientBase::TunnelSessionClientBase(const buzz::Jid& jid, | |
105 SessionManager* manager, const std::string &ns) | |
106 : jid_(jid), session_manager_(manager), namespace_(ns), shutdown_(false) { | |
107 session_manager_->AddClient(namespace_, this); | |
108 } | |
109 | |
110 TunnelSessionClientBase::~TunnelSessionClientBase() { | |
111 shutdown_ = true; | |
112 for (std::vector<TunnelSession*>::iterator it = sessions_.begin(); | |
113 it != sessions_.end(); | |
114 ++it) { | |
115 Session* session = (*it)->ReleaseSession(true); | |
116 session_manager_->DestroySession(session); | |
117 } | |
118 session_manager_->RemoveClient(namespace_); | |
119 } | |
120 | |
121 void TunnelSessionClientBase::OnSessionCreate(Session* session, bool received) { | |
122 LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionCreate: received=" | |
123 << received; | |
124 ASSERT(session_manager_->signaling_thread()->IsCurrent()); | |
125 if (received) | |
126 sessions_.push_back( | |
127 MakeTunnelSession(session, rtc::Thread::Current(), RESPONDER)); | |
128 } | |
129 | |
130 void TunnelSessionClientBase::OnSessionDestroy(Session* session) { | |
131 LOG(LS_INFO) << "TunnelSessionClientBase::OnSessionDestroy"; | |
132 ASSERT(session_manager_->signaling_thread()->IsCurrent()); | |
133 if (shutdown_) | |
134 return; | |
135 for (std::vector<TunnelSession*>::iterator it = sessions_.begin(); | |
136 it != sessions_.end(); | |
137 ++it) { | |
138 if ((*it)->HasSession(session)) { | |
139 VERIFY((*it)->ReleaseSession(false) == session); | |
140 sessions_.erase(it); | |
141 return; | |
142 } | |
143 } | |
144 } | |
145 | |
146 rtc::StreamInterface* TunnelSessionClientBase::CreateTunnel( | |
147 const buzz::Jid& to, const std::string& description) { | |
148 // Valid from any thread | |
149 CreateTunnelData data; | |
150 data.jid = to; | |
151 data.description = description; | |
152 data.thread = rtc::Thread::Current(); | |
153 data.stream = NULL; | |
154 session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data); | |
155 return data.stream; | |
156 } | |
157 | |
158 rtc::StreamInterface* TunnelSessionClientBase::AcceptTunnel( | |
159 Session* session) { | |
160 ASSERT(session_manager_->signaling_thread()->IsCurrent()); | |
161 TunnelSession* tunnel = NULL; | |
162 for (std::vector<TunnelSession*>::iterator it = sessions_.begin(); | |
163 it != sessions_.end(); | |
164 ++it) { | |
165 if ((*it)->HasSession(session)) { | |
166 tunnel = *it; | |
167 break; | |
168 } | |
169 } | |
170 ASSERT(tunnel != NULL); | |
171 | |
172 SessionDescription* answer = CreateAnswer(session->remote_description()); | |
173 if (answer == NULL) | |
174 return NULL; | |
175 | |
176 session->Accept(answer); | |
177 return tunnel->GetStream(); | |
178 } | |
179 | |
180 void TunnelSessionClientBase::DeclineTunnel(Session* session) { | |
181 ASSERT(session_manager_->signaling_thread()->IsCurrent()); | |
182 session->Reject(STR_TERMINATE_DECLINE); | |
183 } | |
184 | |
185 void TunnelSessionClientBase::OnMessage(rtc::Message* pmsg) { | |
186 if (pmsg->message_id == MSG_CREATE_TUNNEL) { | |
187 ASSERT(session_manager_->signaling_thread()->IsCurrent()); | |
188 CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata); | |
189 SessionDescription* offer = CreateOffer(data->jid, data->description); | |
190 if (offer == NULL) { | |
191 return; | |
192 } | |
193 | |
194 Session* session = session_manager_->CreateSession(jid_.Str(), namespace_); | |
195 TunnelSession* tunnel = MakeTunnelSession(session, data->thread, | |
196 INITIATOR); | |
197 sessions_.push_back(tunnel); | |
198 session->Initiate(data->jid.Str(), offer); | |
199 data->stream = tunnel->GetStream(); | |
200 } | |
201 } | |
202 | |
203 TunnelSession* TunnelSessionClientBase::MakeTunnelSession( | |
204 Session* session, rtc::Thread* stream_thread, | |
205 TunnelSessionRole /*role*/) { | |
206 return new TunnelSession(this, session, stream_thread); | |
207 } | |
208 | |
209 /////////////////////////////////////////////////////////////////////////////// | |
210 // TunnelSessionClient | |
211 /////////////////////////////////////////////////////////////////////////////// | |
212 | |
213 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid, | |
214 SessionManager* manager, | |
215 const std::string &ns) | |
216 : TunnelSessionClientBase(jid, manager, ns) { | |
217 } | |
218 | |
219 TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid, | |
220 SessionManager* manager) | |
221 : TunnelSessionClientBase(jid, manager, NS_TUNNEL) { | |
222 } | |
223 | |
224 TunnelSessionClient::~TunnelSessionClient() { | |
225 } | |
226 | |
227 | |
228 bool TunnelSessionClient::ParseContent(SignalingProtocol protocol, | |
229 const buzz::XmlElement* elem, | |
230 ContentDescription** content, | |
231 ParseError* error) { | |
232 if (const buzz::XmlElement* type_elem = elem->FirstNamed(QN_TUNNEL_TYPE)) { | |
233 *content = new TunnelContentDescription(type_elem->BodyText()); | |
234 return true; | |
235 } | |
236 return false; | |
237 } | |
238 | |
239 bool TunnelSessionClient::WriteContent( | |
240 SignalingProtocol protocol, | |
241 const ContentDescription* untyped_content, | |
242 buzz::XmlElement** elem, WriteError* error) { | |
243 const TunnelContentDescription* content = | |
244 static_cast<const TunnelContentDescription*>(untyped_content); | |
245 | |
246 buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true); | |
247 buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE); | |
248 type_elem->SetBodyText(content->description); | |
249 root->AddElement(type_elem); | |
250 *elem = root; | |
251 return true; | |
252 } | |
253 | |
254 SessionDescription* NewTunnelSessionDescription( | |
255 const std::string& content_name, ContentDescription* content) { | |
256 SessionDescription* sdesc = new SessionDescription(); | |
257 sdesc->AddContent(content_name, NS_TUNNEL, content); | |
258 return sdesc; | |
259 } | |
260 | |
261 bool FindTunnelContent(const cricket::SessionDescription* sdesc, | |
262 std::string* name, | |
263 const TunnelContentDescription** content) { | |
264 const ContentInfo* cinfo = sdesc->FirstContentByType(NS_TUNNEL); | |
265 if (cinfo == NULL) | |
266 return false; | |
267 | |
268 *name = cinfo->name; | |
269 *content = static_cast<const TunnelContentDescription*>( | |
270 cinfo->description); | |
271 return true; | |
272 } | |
273 | |
274 void TunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid, | |
275 Session *session) { | |
276 std::string content_name; | |
277 const TunnelContentDescription* content = NULL; | |
278 if (!FindTunnelContent(session->remote_description(), | |
279 &content_name, &content)) { | |
280 session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS); | |
281 return; | |
282 } | |
283 | |
284 SignalIncomingTunnel(this, jid, content->description, session); | |
285 } | |
286 | |
287 SessionDescription* TunnelSessionClient::CreateOffer( | |
288 const buzz::Jid &jid, const std::string &description) { | |
289 SessionDescription* offer = NewTunnelSessionDescription( | |
290 CN_TUNNEL, new TunnelContentDescription(description)); | |
291 rtc::scoped_ptr<TransportDescription> tdesc( | |
292 session_manager_->transport_desc_factory()->CreateOffer( | |
293 TransportOptions(), NULL)); | |
294 if (tdesc.get()) { | |
295 offer->AddTransportInfo(TransportInfo(CN_TUNNEL, *tdesc)); | |
296 } else { | |
297 delete offer; | |
298 offer = NULL; | |
299 } | |
300 return offer; | |
301 } | |
302 | |
303 SessionDescription* TunnelSessionClient::CreateAnswer( | |
304 const SessionDescription* offer) { | |
305 std::string content_name; | |
306 const TunnelContentDescription* offer_tunnel = NULL; | |
307 if (!FindTunnelContent(offer, &content_name, &offer_tunnel)) | |
308 return NULL; | |
309 | |
310 SessionDescription* answer = NewTunnelSessionDescription( | |
311 content_name, new TunnelContentDescription(offer_tunnel->description)); | |
312 const TransportInfo* tinfo = offer->GetTransportInfoByName(content_name); | |
313 if (tinfo) { | |
314 const TransportDescription* offer_tdesc = &tinfo->description; | |
315 ASSERT(offer_tdesc != NULL); | |
316 rtc::scoped_ptr<TransportDescription> tdesc( | |
317 session_manager_->transport_desc_factory()->CreateAnswer( | |
318 offer_tdesc, TransportOptions(), NULL)); | |
319 if (tdesc.get()) { | |
320 answer->AddTransportInfo(TransportInfo(content_name, *tdesc)); | |
321 } else { | |
322 delete answer; | |
323 answer = NULL; | |
324 } | |
325 } | |
326 return answer; | |
327 } | |
328 /////////////////////////////////////////////////////////////////////////////// | |
329 // TunnelSession | |
330 /////////////////////////////////////////////////////////////////////////////// | |
331 | |
332 // | |
333 // Signalling thread methods | |
334 // | |
335 | |
336 TunnelSession::TunnelSession(TunnelSessionClientBase* client, Session* session, | |
337 rtc::Thread* stream_thread) | |
338 : client_(client), session_(session), channel_(NULL) { | |
339 ASSERT(client_ != NULL); | |
340 ASSERT(session_ != NULL); | |
341 session_->SignalState.connect(this, &TunnelSession::OnSessionState); | |
342 channel_ = new PseudoTcpChannel(stream_thread, session_); | |
343 channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed); | |
344 } | |
345 | |
346 TunnelSession::~TunnelSession() { | |
347 ASSERT(client_ != NULL); | |
348 ASSERT(session_ == NULL); | |
349 ASSERT(channel_ == NULL); | |
350 } | |
351 | |
352 rtc::StreamInterface* TunnelSession::GetStream() { | |
353 ASSERT(channel_ != NULL); | |
354 return channel_->GetStream(); | |
355 } | |
356 | |
357 bool TunnelSession::HasSession(Session* session) { | |
358 ASSERT(NULL != session_); | |
359 return (session_ == session); | |
360 } | |
361 | |
362 Session* TunnelSession::ReleaseSession(bool channel_exists) { | |
363 ASSERT(NULL != session_); | |
364 ASSERT(NULL != channel_); | |
365 Session* session = session_; | |
366 session_->SignalState.disconnect(this); | |
367 session_ = NULL; | |
368 if (channel_exists) | |
369 channel_->SignalChannelClosed.disconnect(this); | |
370 channel_ = NULL; | |
371 delete this; | |
372 return session; | |
373 } | |
374 | |
375 void TunnelSession::OnSessionState(BaseSession* session, | |
376 BaseSession::State state) { | |
377 LOG(LS_INFO) << "TunnelSession::OnSessionState(" | |
378 << rtc::nonnull( | |
379 rtc::FindLabel(state, SESSION_STATES), "Unknown") | |
380 << ")"; | |
381 ASSERT(session == session_); | |
382 | |
383 switch (state) { | |
384 case Session::STATE_RECEIVEDINITIATE: | |
385 OnInitiate(); | |
386 break; | |
387 case Session::STATE_SENTACCEPT: | |
388 case Session::STATE_RECEIVEDACCEPT: | |
389 OnAccept(); | |
390 break; | |
391 case Session::STATE_SENTTERMINATE: | |
392 case Session::STATE_RECEIVEDTERMINATE: | |
393 OnTerminate(); | |
394 break; | |
395 case Session::STATE_DEINIT: | |
396 // ReleaseSession should have been called before this. | |
397 ASSERT(false); | |
398 break; | |
399 default: | |
400 break; | |
401 } | |
402 } | |
403 | |
404 void TunnelSession::OnInitiate() { | |
405 ASSERT(client_ != NULL); | |
406 ASSERT(session_ != NULL); | |
407 client_->OnIncomingTunnel(buzz::Jid(session_->remote_name()), session_); | |
408 } | |
409 | |
410 void TunnelSession::OnAccept() { | |
411 ASSERT(channel_ != NULL); | |
412 const ContentInfo* content = | |
413 session_->remote_description()->FirstContentByType(NS_TUNNEL); | |
414 ASSERT(content != NULL); | |
415 VERIFY(channel_->Connect( | |
416 content->name, "tcp", ICE_CANDIDATE_COMPONENT_DEFAULT)); | |
417 } | |
418 | |
419 void TunnelSession::OnTerminate() { | |
420 ASSERT(channel_ != NULL); | |
421 channel_->OnSessionTerminate(session_); | |
422 } | |
423 | |
424 void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) { | |
425 ASSERT(channel_ == channel); | |
426 ASSERT(session_ != NULL); | |
427 session_->Terminate(); | |
428 } | |
429 | |
430 /////////////////////////////////////////////////////////////////////////////// | |
431 | |
432 } // namespace cricket | |
OLD | NEW |