OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "remoting/jingle_glue/xmpp_socket_adapter.h" | |
6 | |
7 #include <iomanip> | |
8 #include <string> | |
9 | |
10 #include "base/logging.h" | |
11 #include "remoting/jingle_glue/ssl_adapter.h" | |
12 #include "third_party/libjingle/source/talk/base/byteorder.h" | |
13 #include "third_party/libjingle/source/talk/base/common.h" | |
14 #include "third_party/libjingle/source/talk/base/firewallsocketserver.h" | |
15 #include "third_party/libjingle/source/talk/base/socketadapters.h" | |
16 #include "third_party/libjingle/source/talk/base/ssladapter.h" | |
17 #include "third_party/libjingle/source/talk/base/thread.h" | |
18 #include "third_party/libjingle/source/talk/xmpp/xmppengine.h" | |
19 | |
20 namespace remoting { | |
21 | |
22 XmppSocketAdapter::XmppSocketAdapter(const buzz::XmppClientSettings& xcs, | |
23 bool allow_unverified_certs) | |
24 : state_(STATE_CLOSED), | |
25 error_(ERROR_NONE), | |
26 wsa_error_(0), | |
27 socket_(NULL), | |
28 protocol_(xcs.protocol()), | |
29 firewall_(false), | |
30 write_buffer_(NULL), | |
31 write_buffer_length_(0), | |
32 write_buffer_capacity_(0), | |
33 allow_unverified_certs_(allow_unverified_certs) { | |
34 proxy_.type = xcs.proxy(); | |
35 proxy_.address.SetIP(xcs.proxy_host()); | |
36 proxy_.address.SetPort(xcs.proxy_port()); | |
37 proxy_.username = xcs.proxy_user(); | |
38 proxy_.password = xcs.proxy_pass(); | |
39 } | |
40 | |
41 XmppSocketAdapter::~XmppSocketAdapter() { | |
42 FreeState(); | |
43 | |
44 // Clean up any previous socket - cannot delete socket on close because close | |
45 // happens during the child socket's stack callback. | |
46 if (socket_) { | |
47 delete socket_; | |
48 socket_ = NULL; | |
49 } | |
50 } | |
51 | |
52 buzz::AsyncSocket::State XmppSocketAdapter::state() { | |
53 return state_; | |
54 } | |
55 | |
56 buzz::AsyncSocket::Error XmppSocketAdapter::error() { | |
57 return error_; | |
58 } | |
59 | |
60 int XmppSocketAdapter::GetError() { | |
61 return wsa_error_; | |
62 } | |
63 | |
64 bool XmppSocketAdapter::FreeState() { | |
65 int code = 0; | |
66 | |
67 // Clean up the socket. | |
68 if (socket_ && !(state_ == STATE_CLOSED || state_ == STATE_CLOSING)) { | |
69 code = socket_->Close(); | |
70 } | |
71 | |
72 delete[] write_buffer_; | |
73 write_buffer_ = NULL; | |
74 write_buffer_length_ = 0; | |
75 write_buffer_capacity_ = 0; | |
76 | |
77 if (code) { | |
78 SetWSAError(code); | |
79 return false; | |
80 } | |
81 return true; | |
82 } | |
83 | |
84 bool XmppSocketAdapter::Connect(const talk_base::SocketAddress& addr) { | |
85 if (state_ != STATE_CLOSED) { | |
86 SetError(ERROR_WRONGSTATE); | |
87 return false; | |
88 } | |
89 | |
90 VLOG(1) << "XmppSocketAdapter::Connect(" << addr.ToString() << ")"; | |
91 | |
92 // Clean up any previous socket - cannot delete socket on close because close | |
93 // happens during the child socket's stack callback. | |
94 if (socket_) { | |
95 delete socket_; | |
96 socket_ = NULL; | |
97 } | |
98 | |
99 talk_base::AsyncSocket* socket = | |
100 talk_base::Thread::Current()->socketserver()->CreateAsyncSocket( | |
101 SOCK_STREAM); | |
102 if (!socket) { | |
103 SetWSAError(WSA_NOT_ENOUGH_MEMORY); | |
104 return false; | |
105 } | |
106 | |
107 if (firewall_) { | |
108 // TODO(sync): Change this to make WSAAsyncSockets support current thread | |
109 // socket server. | |
110 talk_base::FirewallSocketServer* fw = | |
111 static_cast<talk_base::FirewallSocketServer*>( | |
112 talk_base::Thread::Current()->socketserver()); | |
113 socket = fw->WrapSocket(socket, SOCK_STREAM); | |
114 } | |
115 | |
116 if (proxy_.type) { | |
117 talk_base::AsyncSocket* proxy_socket = 0; | |
118 if (proxy_.type == talk_base::PROXY_SOCKS5) { | |
119 proxy_socket = new talk_base::AsyncSocksProxySocket( | |
120 socket, proxy_.address, proxy_.username, proxy_.password); | |
121 } else { | |
122 // Note: we are trying unknown proxies as HTTPS currently. | |
123 proxy_socket = new talk_base::AsyncHttpsProxySocket(socket, | |
124 "chromoting", proxy_.address, proxy_.username, | |
125 proxy_.password); | |
126 } | |
127 if (!proxy_socket) { | |
128 SetWSAError(WSA_NOT_ENOUGH_MEMORY); | |
129 delete socket; | |
130 return false; | |
131 } | |
132 socket = proxy_socket; // For our purposes the proxy is now the socket. | |
133 } | |
134 | |
135 if (protocol_ == cricket::PROTO_SSLTCP) { | |
136 talk_base::AsyncSocket *fake_ssl_socket = | |
137 new talk_base::AsyncSSLSocket(socket); | |
138 if (!fake_ssl_socket) { | |
139 SetWSAError(WSA_NOT_ENOUGH_MEMORY); | |
140 delete socket; | |
141 return false; | |
142 } | |
143 socket = fake_ssl_socket; // For our purposes the SSL socket is the socket. | |
144 } | |
145 | |
146 #if defined(FEATURE_ENABLE_SSL) | |
147 talk_base::SSLAdapter* ssl_adapter = remoting::CreateSSLAdapter(socket); | |
148 socket = ssl_adapter; // For our purposes the SSL adapter is the socket. | |
149 #endif | |
150 | |
151 socket->SignalReadEvent.connect(this, &XmppSocketAdapter::OnReadEvent); | |
152 socket->SignalWriteEvent.connect(this, &XmppSocketAdapter::OnWriteEvent); | |
153 socket->SignalConnectEvent.connect(this, &XmppSocketAdapter::OnConnectEvent); | |
154 socket->SignalCloseEvent.connect(this, &XmppSocketAdapter::OnCloseEvent); | |
155 | |
156 // The linux implementation of socket::Connect returns an error when the | |
157 // connect didn't complete yet. This can be distinguished from a failure | |
158 // because socket::IsBlocking is true. Perhaps, the linux implementation | |
159 // should be made to behave like the windows version which doesn't do this, | |
160 // but it seems to be a pattern with these methods that they return an error | |
161 // if the operation didn't complete in a sync fashion and one has to check | |
162 // IsBlocking to tell if was a "real" error. | |
163 if (socket->Connect(addr) == SOCKET_ERROR && !socket->IsBlocking()) { | |
164 SetWSAError(socket->GetError()); | |
165 delete socket; | |
166 return false; | |
167 } | |
168 | |
169 socket_ = socket; | |
170 state_ = STATE_CONNECTING; | |
171 return true; | |
172 } | |
173 | |
174 bool XmppSocketAdapter::Read(char* data, size_t len, size_t* len_read) { | |
175 if (len_read) | |
176 *len_read = 0; | |
177 | |
178 if (state_ <= STATE_CLOSING) { | |
179 SetError(ERROR_WRONGSTATE); | |
180 return false; | |
181 } | |
182 | |
183 DCHECK(socket_); | |
184 | |
185 if (IsOpen()) { | |
186 int result = socket_->Recv(data, len); | |
187 if (result < 0) { | |
188 if (!socket_->IsBlocking()) { | |
189 SetWSAError(socket_->GetError()); | |
190 return false; | |
191 } | |
192 | |
193 result = 0; | |
194 } | |
195 | |
196 if (len_read) | |
197 *len_read = result; | |
198 } | |
199 | |
200 return true; | |
201 } | |
202 | |
203 bool XmppSocketAdapter::Write(const char* data, size_t len) { | |
204 if (state_ <= STATE_CLOSING) { | |
205 // There may be data in a buffer that gets lost. Too bad! | |
206 SetError(ERROR_WRONGSTATE); | |
207 return false; | |
208 } | |
209 | |
210 DCHECK(socket_); | |
211 | |
212 size_t sent = 0; | |
213 | |
214 // Try an immediate write when there is no buffer and we aren't in SSL mode | |
215 // or opening the connection. | |
216 if (write_buffer_length_ == 0 && IsOpen()) { | |
217 int result = socket_->Send(data, len); | |
218 if (result < 0) { | |
219 if (!socket_->IsBlocking()) { | |
220 SetWSAError(socket_->GetError()); | |
221 return false; | |
222 } | |
223 result = 0; | |
224 } | |
225 | |
226 sent = static_cast<size_t>(result); | |
227 } | |
228 | |
229 // Buffer what we didn't send. | |
230 if (sent < len) { | |
231 QueueWriteData(data + sent, len - sent); | |
232 } | |
233 | |
234 // Service the socket right away to push the written data out in SSL mode. | |
235 return HandleWritable(); | |
236 } | |
237 | |
238 bool XmppSocketAdapter::Close() { | |
239 if (state_ == STATE_CLOSING) { | |
240 return false; // Avoid recursion, but not unexpected. | |
241 } | |
242 if (state_ == STATE_CLOSED) { | |
243 // In theory should not be trying to re-InternalClose. | |
244 SetError(ERROR_WRONGSTATE); | |
245 return false; | |
246 } | |
247 | |
248 // TODO(sync): deal with flushing close (flush, don't do reads, clean ssl). | |
249 | |
250 // If we've gotten to the point where we really do have a socket underneath | |
251 // then close it. It should call us back to tell us it is closed, and | |
252 // NotifyClose will be called. We indicate "closing" state so that we | |
253 // do not recusively try to keep closing the socket. | |
254 if (socket_) { | |
255 state_ = STATE_CLOSING; | |
256 socket_->Close(); | |
257 } | |
258 | |
259 // If we didn't get the callback, then we better make sure we signal | |
260 // closed. | |
261 if (state_ != STATE_CLOSED) { | |
262 // The socket was closed manually, not directly due to error. | |
263 if (error_ != ERROR_NONE) { | |
264 VLOG(1) << "XmppSocketAdapter::Close - previous Error: " << error_ | |
265 << " WSAError: " << wsa_error_; | |
266 error_ = ERROR_NONE; | |
267 wsa_error_ = 0; | |
268 } | |
269 NotifyClose(); | |
270 } | |
271 return true; | |
272 } | |
273 | |
274 void XmppSocketAdapter::NotifyClose() { | |
275 if (state_ == STATE_CLOSED) { | |
276 SetError(ERROR_WRONGSTATE); | |
277 } else { | |
278 VLOG(1) << "XmppSocketAdapter::NotifyClose - Error: " << error_ | |
279 << " WSAError: " << wsa_error_; | |
280 state_ = STATE_CLOSED; | |
281 SignalClosed(); | |
282 FreeState(); | |
283 } | |
284 } | |
285 | |
286 void XmppSocketAdapter::OnConnectEvent(talk_base::AsyncSocket *socket) { | |
287 if (state_ == STATE_CONNECTING) { | |
288 state_ = STATE_OPEN; | |
289 VLOG(1) << "XmppSocketAdapter::OnConnectEvent - STATE_OPEN"; | |
290 SignalConnected(); | |
291 #if defined(FEATURE_ENABLE_SSL) | |
292 } else if (state_ == STATE_TLS_CONNECTING) { | |
293 state_ = STATE_TLS_OPEN; | |
294 VLOG(1) << "XmppSocketAdapter::OnConnectEvent - STATE_TLS_OPEN"; | |
295 SignalSSLConnected(); | |
296 if (write_buffer_length_ > 0) { | |
297 HandleWritable(); | |
298 } | |
299 #endif // defined(FEATURE_ENABLE_SSL) | |
300 } else { | |
301 LOG(DFATAL) << "unexpected XmppSocketAdapter::OnConnectEvent state: " | |
302 << state_; | |
303 } | |
304 } | |
305 | |
306 void XmppSocketAdapter::OnReadEvent(talk_base::AsyncSocket *socket) { | |
307 HandleReadable(); | |
308 } | |
309 | |
310 void XmppSocketAdapter::OnWriteEvent(talk_base::AsyncSocket *socket) { | |
311 HandleWritable(); | |
312 } | |
313 | |
314 void XmppSocketAdapter::OnCloseEvent(talk_base::AsyncSocket *socket, | |
315 int error) { | |
316 VLOG(1) << "XmppSocketAdapter::OnCloseEvent(" << error << ")"; | |
317 SetWSAError(error); | |
318 if (error == SOCKET_EACCES) { | |
319 SignalAuthenticationError(); // Proxy needs authentication. | |
320 } | |
321 NotifyClose(); | |
322 } | |
323 | |
324 #if defined(FEATURE_ENABLE_SSL) | |
325 bool XmppSocketAdapter::StartTls(const std::string& verify_host_name) { | |
326 if (state_ != STATE_OPEN) { | |
327 SetError(ERROR_WRONGSTATE); | |
328 return false; | |
329 } | |
330 | |
331 state_ = STATE_TLS_CONNECTING; | |
332 | |
333 DCHECK_EQ(write_buffer_length_, 0U); | |
334 | |
335 talk_base::SSLAdapter* ssl_adapter = | |
336 static_cast<talk_base::SSLAdapter*>(socket_); | |
337 | |
338 if (allow_unverified_certs_) { | |
339 ssl_adapter->set_ignore_bad_cert(true); | |
340 } | |
341 | |
342 if (ssl_adapter->StartSSL(verify_host_name.c_str(), false) != 0) { | |
343 state_ = STATE_OPEN; | |
344 SetError(ERROR_SSL); | |
345 return false; | |
346 } | |
347 | |
348 return true; | |
349 } | |
350 #endif // defined(FEATURE_ENABLE_SSL) | |
351 | |
352 void XmppSocketAdapter::QueueWriteData(const char* data, size_t len) { | |
353 // Expand buffer if needed. | |
354 if (write_buffer_length_ + len > write_buffer_capacity_) { | |
355 size_t new_capacity = 1024; | |
356 while (new_capacity < write_buffer_length_ + len) { | |
357 new_capacity = new_capacity * 2; | |
358 } | |
359 char* new_buffer = new char[new_capacity]; | |
360 DCHECK_LE(write_buffer_length_, 64000U); | |
361 memcpy(new_buffer, write_buffer_, write_buffer_length_); | |
362 delete[] write_buffer_; | |
363 write_buffer_ = new_buffer; | |
364 write_buffer_capacity_ = new_capacity; | |
365 } | |
366 | |
367 // Copy data into the end of buffer. | |
368 memcpy(write_buffer_ + write_buffer_length_, data, len); | |
369 write_buffer_length_ += len; | |
370 } | |
371 | |
372 void XmppSocketAdapter::FlushWriteQueue(Error* error, int* wsa_error) { | |
373 DCHECK(error); | |
374 DCHECK(wsa_error); | |
375 | |
376 size_t flushed = 0; | |
377 while (flushed < write_buffer_length_) { | |
378 int sent = socket_->Send(write_buffer_ + flushed, | |
379 static_cast<int>(write_buffer_length_ - flushed)); | |
380 if (sent < 0) { | |
381 if (!socket_->IsBlocking()) { | |
382 *error = ERROR_WINSOCK; | |
383 *wsa_error = socket_->GetError(); | |
384 } | |
385 break; | |
386 } | |
387 flushed += static_cast<size_t>(sent); | |
388 } | |
389 | |
390 // Remove flushed memory. | |
391 write_buffer_length_ -= flushed; | |
392 memmove(write_buffer_, write_buffer_ + flushed, write_buffer_length_); | |
393 | |
394 // When everything is flushed, deallocate the buffer if it's gotten big. | |
395 if (write_buffer_length_ == 0) { | |
396 if (write_buffer_capacity_ > 8192) { | |
397 delete[] write_buffer_; | |
398 write_buffer_ = NULL; | |
399 write_buffer_capacity_ = 0; | |
400 } | |
401 } | |
402 } | |
403 | |
404 void XmppSocketAdapter::SetError(Error error) { | |
405 if (error_ == ERROR_NONE) { | |
406 error_ = error; | |
407 } | |
408 } | |
409 | |
410 void XmppSocketAdapter::SetWSAError(int error) { | |
411 if (error_ == ERROR_NONE && error != 0) { | |
412 error_ = ERROR_WINSOCK; | |
413 wsa_error_ = error; | |
414 } | |
415 } | |
416 | |
417 bool XmppSocketAdapter::HandleReadable() { | |
418 if (!IsOpen()) | |
419 return false; | |
420 | |
421 SignalRead(); | |
422 return true; | |
423 } | |
424 | |
425 bool XmppSocketAdapter::HandleWritable() { | |
426 if (!IsOpen()) | |
427 return false; | |
428 | |
429 Error error = ERROR_NONE; | |
430 int wsa_error = 0; | |
431 FlushWriteQueue(&error, &wsa_error); | |
432 if (error != ERROR_NONE) { | |
433 Close(); | |
434 return false; | |
435 } | |
436 return true; | |
437 } | |
438 | |
439 } // namespace remoting | |
OLD | NEW |