1/*
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 */
10
11#include "xmppsocket.h"
12
13#ifdef HAVE_CONFIG_H
14#include <config.h>
15#endif
16
17#include <errno.h>
18#include "webrtc/base/logging.h"
19#include "webrtc/base/thread.h"
20#ifdef FEATURE_ENABLE_SSL
21#include "webrtc/base/ssladapter.h"
22#endif
23
24#ifdef USE_SSLSTREAM
25#include "webrtc/base/socketstream.h"
26#ifdef FEATURE_ENABLE_SSL
27#include "webrtc/base/sslstreamadapter.h"
28#endif  // FEATURE_ENABLE_SSL
29#endif  // USE_SSLSTREAM
30
31namespace buzz {
32
33XmppSocket::XmppSocket(buzz::TlsOptions tls) : cricket_socket_(NULL),
34                                               tls_(tls) {
35  state_ = buzz::AsyncSocket::STATE_CLOSED;
36}
37
38void XmppSocket::CreateCricketSocket(int family) {
39  rtc::Thread* pth = rtc::Thread::Current();
40  if (family == AF_UNSPEC) {
41    family = AF_INET;
42  }
43  rtc::AsyncSocket* socket =
44      pth->socketserver()->CreateAsyncSocket(family, SOCK_STREAM);
45#ifndef USE_SSLSTREAM
46#ifdef FEATURE_ENABLE_SSL
47  if (tls_ != buzz::TLS_DISABLED) {
48    socket = rtc::SSLAdapter::Create(socket);
49  }
50#endif  // FEATURE_ENABLE_SSL
51  cricket_socket_ = socket;
52  cricket_socket_->SignalReadEvent.connect(this, &XmppSocket::OnReadEvent);
53  cricket_socket_->SignalWriteEvent.connect(this, &XmppSocket::OnWriteEvent);
54  cricket_socket_->SignalConnectEvent.connect(this,
55                                              &XmppSocket::OnConnectEvent);
56  cricket_socket_->SignalCloseEvent.connect(this, &XmppSocket::OnCloseEvent);
57#else  // USE_SSLSTREAM
58  cricket_socket_ = socket;
59  stream_ = new rtc::SocketStream(cricket_socket_);
60#ifdef FEATURE_ENABLE_SSL
61  if (tls_ != buzz::TLS_DISABLED)
62    stream_ = rtc::SSLStreamAdapter::Create(stream_);
63#endif  // FEATURE_ENABLE_SSL
64  stream_->SignalEvent.connect(this, &XmppSocket::OnEvent);
65#endif  // USE_SSLSTREAM
66}
67
68XmppSocket::~XmppSocket() {
69  Close();
70#ifndef USE_SSLSTREAM
71  delete cricket_socket_;
72#else  // USE_SSLSTREAM
73  delete stream_;
74#endif  // USE_SSLSTREAM
75}
76
77#ifndef USE_SSLSTREAM
78void XmppSocket::OnReadEvent(rtc::AsyncSocket * socket) {
79  SignalRead();
80}
81
82void XmppSocket::OnWriteEvent(rtc::AsyncSocket * socket) {
83  // Write bytes if there are any
84  while (buffer_.Length() != 0) {
85    int written = cricket_socket_->Send(buffer_.Data(), buffer_.Length());
86    if (written > 0) {
87      buffer_.Consume(written);
88      continue;
89    }
90    if (!cricket_socket_->IsBlocking())
91      LOG(LS_ERROR) << "Send error: " << cricket_socket_->GetError();
92    return;
93  }
94}
95
96void XmppSocket::OnConnectEvent(rtc::AsyncSocket * socket) {
97#if defined(FEATURE_ENABLE_SSL)
98  if (state_ == buzz::AsyncSocket::STATE_TLS_CONNECTING) {
99    state_ = buzz::AsyncSocket::STATE_TLS_OPEN;
100    SignalSSLConnected();
101    OnWriteEvent(cricket_socket_);
102    return;
103  }
104#endif  // !defined(FEATURE_ENABLE_SSL)
105  state_ = buzz::AsyncSocket::STATE_OPEN;
106  SignalConnected();
107}
108
109void XmppSocket::OnCloseEvent(rtc::AsyncSocket * socket, int error) {
110  SignalCloseEvent(error);
111}
112
113#else  // USE_SSLSTREAM
114
115void XmppSocket::OnEvent(rtc::StreamInterface* stream,
116                         int events, int err) {
117  if ((events & rtc::SE_OPEN)) {
118#if defined(FEATURE_ENABLE_SSL)
119    if (state_ == buzz::AsyncSocket::STATE_TLS_CONNECTING) {
120      state_ = buzz::AsyncSocket::STATE_TLS_OPEN;
121      SignalSSLConnected();
122      events |= rtc::SE_WRITE;
123    } else
124#endif
125    {
126      state_ = buzz::AsyncSocket::STATE_OPEN;
127      SignalConnected();
128    }
129  }
130  if ((events & rtc::SE_READ))
131    SignalRead();
132  if ((events & rtc::SE_WRITE)) {
133    // Write bytes if there are any
134    while (buffer_.Length() != 0) {
135      rtc::StreamResult result;
136      size_t written;
137      int error;
138      result = stream_->Write(buffer_.Data(), buffer_.Length(),
139                              &written, &error);
140      if (result == rtc::SR_ERROR) {
141        LOG(LS_ERROR) << "Send error: " << error;
142        return;
143      }
144      if (result == rtc::SR_BLOCK)
145        return;
146      ASSERT(result == rtc::SR_SUCCESS);
147      ASSERT(written > 0);
148      buffer_.Shift(written);
149    }
150  }
151  if ((events & rtc::SE_CLOSE))
152    SignalCloseEvent(err);
153}
154#endif  // USE_SSLSTREAM
155
156buzz::AsyncSocket::State XmppSocket::state() {
157  return state_;
158}
159
160buzz::AsyncSocket::Error XmppSocket::error() {
161  return buzz::AsyncSocket::ERROR_NONE;
162}
163
164int XmppSocket::GetError() {
165  return 0;
166}
167
168bool XmppSocket::Connect(const rtc::SocketAddress& addr) {
169  if (cricket_socket_ == NULL) {
170    CreateCricketSocket(addr.family());
171  }
172  if (cricket_socket_->Connect(addr) < 0) {
173    return cricket_socket_->IsBlocking();
174  }
175  return true;
176}
177
178bool XmppSocket::Read(char * data, size_t len, size_t* len_read) {
179#ifndef USE_SSLSTREAM
180  int read = cricket_socket_->Recv(data, len);
181  if (read > 0) {
182    *len_read = (size_t)read;
183    return true;
184  }
185#else  // USE_SSLSTREAM
186  rtc::StreamResult result = stream_->Read(data, len, len_read, NULL);
187  if (result == rtc::SR_SUCCESS)
188    return true;
189#endif  // USE_SSLSTREAM
190  return false;
191}
192
193bool XmppSocket::Write(const char * data, size_t len) {
194  buffer_.WriteBytes(data, len);
195#ifndef USE_SSLSTREAM
196  OnWriteEvent(cricket_socket_);
197#else  // USE_SSLSTREAM
198  OnEvent(stream_, rtc::SE_WRITE, 0);
199#endif  // USE_SSLSTREAM
200  return true;
201}
202
203bool XmppSocket::Close() {
204  if (state_ != buzz::AsyncSocket::STATE_OPEN)
205    return false;
206#ifndef USE_SSLSTREAM
207  if (cricket_socket_->Close() == 0) {
208    state_ = buzz::AsyncSocket::STATE_CLOSED;
209    SignalClosed();
210    return true;
211  }
212  return false;
213#else  // USE_SSLSTREAM
214  state_ = buzz::AsyncSocket::STATE_CLOSED;
215  stream_->Close();
216  SignalClosed();
217  return true;
218#endif  // USE_SSLSTREAM
219}
220
221bool XmppSocket::StartTls(const std::string & domainname) {
222#if defined(FEATURE_ENABLE_SSL)
223  if (tls_ == buzz::TLS_DISABLED)
224    return false;
225#ifndef USE_SSLSTREAM
226  rtc::SSLAdapter* ssl_adapter =
227    static_cast<rtc::SSLAdapter *>(cricket_socket_);
228  if (ssl_adapter->StartSSL(domainname.c_str(), false) != 0)
229    return false;
230#else  // USE_SSLSTREAM
231  rtc::SSLStreamAdapter* ssl_stream =
232    static_cast<rtc::SSLStreamAdapter *>(stream_);
233  if (ssl_stream->StartSSLWithServer(domainname.c_str()) != 0)
234    return false;
235#endif  // USE_SSLSTREAM
236  state_ = buzz::AsyncSocket::STATE_TLS_CONNECTING;
237  return true;
238#else  // !defined(FEATURE_ENABLE_SSL)
239  return false;
240#endif  // !defined(FEATURE_ENABLE_SSL)
241}
242
243}  // namespace buzz
244
245