Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(619)

Side by Side Diff: remoting/jingle_glue/xmpp_socket_adapter.cc

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

Powered by Google App Engine
This is Rietveld 408576698