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