1/*
2 * libjingle
3 * Copyright 2004--2005, 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 "talk/p2p/base/transport.h"
29#include "talk/p2p/base/transportchannelimpl.h"
30#include "talk/p2p/base/transportchannelproxy.h"
31#include "webrtc/base/common.h"
32#include "webrtc/base/logging.h"
33#include "webrtc/base/thread.h"
34
35namespace cricket {
36
37enum {
38  MSG_UPDATESTATE,
39};
40
41TransportChannelProxy::TransportChannelProxy(const std::string& content_name,
42                                             const std::string& name,
43                                             int component)
44    : TransportChannel(content_name, component),
45      name_(name),
46      impl_(NULL) {
47  worker_thread_ = rtc::Thread::Current();
48}
49
50TransportChannelProxy::~TransportChannelProxy() {
51  // Clearing any pending signal.
52  worker_thread_->Clear(this);
53  if (impl_)
54    impl_->GetTransport()->DestroyChannel(impl_->component());
55}
56
57void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) {
58  ASSERT(rtc::Thread::Current() == worker_thread_);
59
60  if (impl == impl_) {
61    // Ignore if the |impl| has already been set.
62    LOG(LS_WARNING) << "Ignored TransportChannelProxy::SetImplementation call "
63                    << "with a same impl as the existing one.";
64    return;
65  }
66
67  // Destroy any existing impl_.
68  if (impl_) {
69    impl_->GetTransport()->DestroyChannel(impl_->component());
70  }
71
72  // Adopt the supplied impl, and connect to its signals.
73  impl_ = impl;
74
75  if (impl_) {
76    impl_->SignalReadableState.connect(
77        this, &TransportChannelProxy::OnReadableState);
78    impl_->SignalWritableState.connect(
79        this, &TransportChannelProxy::OnWritableState);
80    impl_->SignalReadPacket.connect(
81        this, &TransportChannelProxy::OnReadPacket);
82    impl_->SignalReadyToSend.connect(
83        this, &TransportChannelProxy::OnReadyToSend);
84    impl_->SignalRouteChange.connect(
85        this, &TransportChannelProxy::OnRouteChange);
86    for (OptionList::iterator it = pending_options_.begin();
87         it != pending_options_.end();
88         ++it) {
89      impl_->SetOption(it->first, it->second);
90    }
91
92    // Push down the SRTP ciphers, if any were set.
93    if (!pending_srtp_ciphers_.empty()) {
94      impl_->SetSrtpCiphers(pending_srtp_ciphers_);
95    }
96    pending_options_.clear();
97  }
98
99  // Post ourselves a message to see if we need to fire state callbacks.
100  worker_thread_->Post(this, MSG_UPDATESTATE);
101}
102
103int TransportChannelProxy::SendPacket(const char* data, size_t len,
104                                      const rtc::PacketOptions& options,
105                                      int flags) {
106  ASSERT(rtc::Thread::Current() == worker_thread_);
107  // Fail if we don't have an impl yet.
108  if (!impl_) {
109    return -1;
110  }
111  return impl_->SendPacket(data, len, options, flags);
112}
113
114int TransportChannelProxy::SetOption(rtc::Socket::Option opt, int value) {
115  ASSERT(rtc::Thread::Current() == worker_thread_);
116  if (!impl_) {
117    pending_options_.push_back(OptionPair(opt, value));
118    return 0;
119  }
120  return impl_->SetOption(opt, value);
121}
122
123int TransportChannelProxy::GetError() {
124  ASSERT(rtc::Thread::Current() == worker_thread_);
125  if (!impl_) {
126    return 0;
127  }
128  return impl_->GetError();
129}
130
131bool TransportChannelProxy::GetStats(ConnectionInfos* infos) {
132  ASSERT(rtc::Thread::Current() == worker_thread_);
133  if (!impl_) {
134    return false;
135  }
136  return impl_->GetStats(infos);
137}
138
139bool TransportChannelProxy::IsDtlsActive() const {
140  ASSERT(rtc::Thread::Current() == worker_thread_);
141  if (!impl_) {
142    return false;
143  }
144  return impl_->IsDtlsActive();
145}
146
147bool TransportChannelProxy::GetSslRole(rtc::SSLRole* role) const {
148  ASSERT(rtc::Thread::Current() == worker_thread_);
149  if (!impl_) {
150    return false;
151  }
152  return impl_->GetSslRole(role);
153}
154
155bool TransportChannelProxy::SetSslRole(rtc::SSLRole role) {
156  ASSERT(rtc::Thread::Current() == worker_thread_);
157  if (!impl_) {
158    return false;
159  }
160  return impl_->SetSslRole(role);
161}
162
163bool TransportChannelProxy::SetSrtpCiphers(const std::vector<std::string>&
164                                           ciphers) {
165  ASSERT(rtc::Thread::Current() == worker_thread_);
166  pending_srtp_ciphers_ = ciphers;  // Cache so we can send later, but always
167                                    // set so it stays consistent.
168  if (impl_) {
169    return impl_->SetSrtpCiphers(ciphers);
170  }
171  return true;
172}
173
174bool TransportChannelProxy::GetSrtpCipher(std::string* cipher) {
175  ASSERT(rtc::Thread::Current() == worker_thread_);
176  if (!impl_) {
177    return false;
178  }
179  return impl_->GetSrtpCipher(cipher);
180}
181
182bool TransportChannelProxy::GetLocalIdentity(
183    rtc::SSLIdentity** identity) const {
184  ASSERT(rtc::Thread::Current() == worker_thread_);
185  if (!impl_) {
186    return false;
187  }
188  return impl_->GetLocalIdentity(identity);
189}
190
191bool TransportChannelProxy::GetRemoteCertificate(
192    rtc::SSLCertificate** cert) const {
193  ASSERT(rtc::Thread::Current() == worker_thread_);
194  if (!impl_) {
195    return false;
196  }
197  return impl_->GetRemoteCertificate(cert);
198}
199
200bool TransportChannelProxy::ExportKeyingMaterial(const std::string& label,
201                                                 const uint8* context,
202                                                 size_t context_len,
203                                                 bool use_context,
204                                                 uint8* result,
205                                                 size_t result_len) {
206  ASSERT(rtc::Thread::Current() == worker_thread_);
207  if (!impl_) {
208    return false;
209  }
210  return impl_->ExportKeyingMaterial(label, context, context_len, use_context,
211                                     result, result_len);
212}
213
214IceRole TransportChannelProxy::GetIceRole() const {
215  ASSERT(rtc::Thread::Current() == worker_thread_);
216  if (!impl_) {
217    return ICEROLE_UNKNOWN;
218  }
219  return impl_->GetIceRole();
220}
221
222void TransportChannelProxy::OnReadableState(TransportChannel* channel) {
223  ASSERT(rtc::Thread::Current() == worker_thread_);
224  ASSERT(channel == impl_);
225  set_readable(impl_->readable());
226  // Note: SignalReadableState fired by set_readable.
227}
228
229void TransportChannelProxy::OnWritableState(TransportChannel* channel) {
230  ASSERT(rtc::Thread::Current() == worker_thread_);
231  ASSERT(channel == impl_);
232  set_writable(impl_->writable());
233  // Note: SignalWritableState fired by set_readable.
234}
235
236void TransportChannelProxy::OnReadPacket(
237    TransportChannel* channel, const char* data, size_t size,
238    const rtc::PacketTime& packet_time, int flags) {
239  ASSERT(rtc::Thread::Current() == worker_thread_);
240  ASSERT(channel == impl_);
241  SignalReadPacket(this, data, size, packet_time, flags);
242}
243
244void TransportChannelProxy::OnReadyToSend(TransportChannel* channel) {
245  ASSERT(rtc::Thread::Current() == worker_thread_);
246  ASSERT(channel == impl_);
247  SignalReadyToSend(this);
248}
249
250void TransportChannelProxy::OnRouteChange(TransportChannel* channel,
251                                          const Candidate& candidate) {
252  ASSERT(rtc::Thread::Current() == worker_thread_);
253  ASSERT(channel == impl_);
254  SignalRouteChange(this, candidate);
255}
256
257void TransportChannelProxy::OnMessage(rtc::Message* msg) {
258  ASSERT(rtc::Thread::Current() == worker_thread_);
259  if (msg->message_id == MSG_UPDATESTATE) {
260     // If impl_ is already readable or writable, push up those signals.
261     set_readable(impl_ ? impl_->readable() : false);
262     set_writable(impl_ ? impl_->writable() : false);
263  }
264}
265
266}  // namespace cricket
267