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 "jingle/glue/chrome_async_socket.h"
6
7 #include <algorithm>
8 #include <cstdlib>
9 #include <cstring>
10
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "jingle/glue/resolving_client_socket_factory.h"
17 #include "net/base/address_list.h"
18 #include "net/base/host_port_pair.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_util.h"
21 #include "net/socket/client_socket_handle.h"
22 #include "net/socket/ssl_client_socket.h"
23 #include "net/socket/tcp_client_socket.h"
24 #include "net/ssl/ssl_config_service.h"
25 #include "third_party/libjingle/source/talk/base/socketaddress.h"
26
27 namespace jingle_glue {
28
ChromeAsyncSocket(ResolvingClientSocketFactory * resolving_client_socket_factory,size_t read_buf_size,size_t write_buf_size)29 ChromeAsyncSocket::ChromeAsyncSocket(
30 ResolvingClientSocketFactory* resolving_client_socket_factory,
31 size_t read_buf_size,
32 size_t write_buf_size)
33 : resolving_client_socket_factory_(resolving_client_socket_factory),
34 state_(STATE_CLOSED),
35 error_(ERROR_NONE),
36 net_error_(net::OK),
37 read_state_(IDLE),
38 read_buf_(new net::IOBufferWithSize(read_buf_size)),
39 read_start_(0U),
40 read_end_(0U),
41 write_state_(IDLE),
42 write_buf_(new net::IOBufferWithSize(write_buf_size)),
43 write_end_(0U),
44 weak_ptr_factory_(this) {
45 DCHECK(resolving_client_socket_factory_.get());
46 DCHECK_GT(read_buf_size, 0U);
47 DCHECK_GT(write_buf_size, 0U);
48 }
49
~ChromeAsyncSocket()50 ChromeAsyncSocket::~ChromeAsyncSocket() {}
51
state()52 ChromeAsyncSocket::State ChromeAsyncSocket::state() {
53 return state_;
54 }
55
error()56 ChromeAsyncSocket::Error ChromeAsyncSocket::error() {
57 return error_;
58 }
59
GetError()60 int ChromeAsyncSocket::GetError() {
61 return net_error_;
62 }
63
IsOpen() const64 bool ChromeAsyncSocket::IsOpen() const {
65 return (state_ == STATE_OPEN) || (state_ == STATE_TLS_OPEN);
66 }
67
DoNonNetError(Error error)68 void ChromeAsyncSocket::DoNonNetError(Error error) {
69 DCHECK_NE(error, ERROR_NONE);
70 DCHECK_NE(error, ERROR_WINSOCK);
71 error_ = error;
72 net_error_ = net::OK;
73 }
74
DoNetError(net::Error net_error)75 void ChromeAsyncSocket::DoNetError(net::Error net_error) {
76 error_ = ERROR_WINSOCK;
77 net_error_ = net_error;
78 }
79
DoNetErrorFromStatus(int status)80 void ChromeAsyncSocket::DoNetErrorFromStatus(int status) {
81 DCHECK_LT(status, net::OK);
82 DoNetError(static_cast<net::Error>(status));
83 }
84
85 // STATE_CLOSED -> STATE_CONNECTING
86
Connect(const talk_base::SocketAddress & address)87 bool ChromeAsyncSocket::Connect(const talk_base::SocketAddress& address) {
88 if (state_ != STATE_CLOSED) {
89 LOG(DFATAL) << "Connect() called on non-closed socket";
90 DoNonNetError(ERROR_WRONGSTATE);
91 return false;
92 }
93 if (address.hostname().empty() || address.port() == 0) {
94 DoNonNetError(ERROR_DNS);
95 return false;
96 }
97
98 DCHECK_EQ(state_, buzz::AsyncSocket::STATE_CLOSED);
99 DCHECK_EQ(read_state_, IDLE);
100 DCHECK_EQ(write_state_, IDLE);
101
102 state_ = STATE_CONNECTING;
103
104 DCHECK(!weak_ptr_factory_.HasWeakPtrs());
105 weak_ptr_factory_.InvalidateWeakPtrs();
106
107 net::HostPortPair dest_host_port_pair(address.hostname(), address.port());
108
109 transport_socket_ =
110 resolving_client_socket_factory_->CreateTransportClientSocket(
111 dest_host_port_pair);
112 int status = transport_socket_->Connect(
113 base::Bind(&ChromeAsyncSocket::ProcessConnectDone,
114 weak_ptr_factory_.GetWeakPtr()));
115 if (status != net::ERR_IO_PENDING) {
116 // We defer execution of ProcessConnectDone instead of calling it
117 // directly here as the caller may not expect an error/close to
118 // happen here. This is okay, as from the caller's point of view,
119 // the connect always happens asynchronously.
120 base::MessageLoop* message_loop = base::MessageLoop::current();
121 CHECK(message_loop);
122 message_loop->PostTask(
123 FROM_HERE,
124 base::Bind(&ChromeAsyncSocket::ProcessConnectDone,
125 weak_ptr_factory_.GetWeakPtr(), status));
126 }
127 return true;
128 }
129
130 // STATE_CONNECTING -> STATE_OPEN
131 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
132
ProcessConnectDone(int status)133 void ChromeAsyncSocket::ProcessConnectDone(int status) {
134 DCHECK_NE(status, net::ERR_IO_PENDING);
135 DCHECK_EQ(read_state_, IDLE);
136 DCHECK_EQ(write_state_, IDLE);
137 DCHECK_EQ(state_, STATE_CONNECTING);
138 if (status != net::OK) {
139 DoNetErrorFromStatus(status);
140 DoClose();
141 return;
142 }
143 state_ = STATE_OPEN;
144 PostDoRead();
145 // Write buffer should be empty.
146 DCHECK_EQ(write_end_, 0U);
147 SignalConnected();
148 }
149
150 // read_state_ == IDLE -> read_state_ == POSTED
151
PostDoRead()152 void ChromeAsyncSocket::PostDoRead() {
153 DCHECK(IsOpen());
154 DCHECK_EQ(read_state_, IDLE);
155 DCHECK_EQ(read_start_, 0U);
156 DCHECK_EQ(read_end_, 0U);
157 base::MessageLoop* message_loop = base::MessageLoop::current();
158 CHECK(message_loop);
159 message_loop->PostTask(
160 FROM_HERE,
161 base::Bind(&ChromeAsyncSocket::DoRead,
162 weak_ptr_factory_.GetWeakPtr()));
163 read_state_ = POSTED;
164 }
165
166 // read_state_ == POSTED -> read_state_ == PENDING
167
DoRead()168 void ChromeAsyncSocket::DoRead() {
169 DCHECK(IsOpen());
170 DCHECK_EQ(read_state_, POSTED);
171 DCHECK_EQ(read_start_, 0U);
172 DCHECK_EQ(read_end_, 0U);
173 // Once we call Read(), we cannot call StartTls() until the read
174 // finishes. This is okay, as StartTls() is called only from a read
175 // handler (i.e., after a read finishes and before another read is
176 // done).
177 int status =
178 transport_socket_->Read(
179 read_buf_.get(), read_buf_->size(),
180 base::Bind(&ChromeAsyncSocket::ProcessReadDone,
181 weak_ptr_factory_.GetWeakPtr()));
182 read_state_ = PENDING;
183 if (status != net::ERR_IO_PENDING) {
184 ProcessReadDone(status);
185 }
186 }
187
188 // read_state_ == PENDING -> read_state_ == IDLE
189
ProcessReadDone(int status)190 void ChromeAsyncSocket::ProcessReadDone(int status) {
191 DCHECK_NE(status, net::ERR_IO_PENDING);
192 DCHECK(IsOpen());
193 DCHECK_EQ(read_state_, PENDING);
194 DCHECK_EQ(read_start_, 0U);
195 DCHECK_EQ(read_end_, 0U);
196 read_state_ = IDLE;
197 if (status > 0) {
198 read_end_ = static_cast<size_t>(status);
199 SignalRead();
200 } else if (status == 0) {
201 // Other side closed the connection.
202 error_ = ERROR_NONE;
203 net_error_ = net::OK;
204 DoClose();
205 } else { // status < 0
206 DoNetErrorFromStatus(status);
207 DoClose();
208 }
209 }
210
211 // (maybe) read_state_ == IDLE -> read_state_ == POSTED (via
212 // PostDoRead())
213
Read(char * data,size_t len,size_t * len_read)214 bool ChromeAsyncSocket::Read(char* data, size_t len, size_t* len_read) {
215 if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) {
216 // Read() may be called on a closed socket if a previous read
217 // causes a socket close (e.g., client sends wrong password and
218 // server terminates connection).
219 //
220 // TODO(akalin): Fix handling of this on the libjingle side.
221 if (state_ != STATE_CLOSED) {
222 LOG(DFATAL) << "Read() called on non-open non-tls-connecting socket";
223 }
224 DoNonNetError(ERROR_WRONGSTATE);
225 return false;
226 }
227 DCHECK_LE(read_start_, read_end_);
228 if ((state_ == STATE_TLS_CONNECTING) || read_end_ == 0U) {
229 if (state_ == STATE_TLS_CONNECTING) {
230 DCHECK_EQ(read_state_, IDLE);
231 DCHECK_EQ(read_end_, 0U);
232 } else {
233 DCHECK_NE(read_state_, IDLE);
234 }
235 *len_read = 0;
236 return true;
237 }
238 DCHECK_EQ(read_state_, IDLE);
239 *len_read = std::min(len, read_end_ - read_start_);
240 DCHECK_GT(*len_read, 0U);
241 std::memcpy(data, read_buf_->data() + read_start_, *len_read);
242 read_start_ += *len_read;
243 if (read_start_ == read_end_) {
244 read_start_ = 0U;
245 read_end_ = 0U;
246 // We defer execution of DoRead() here for similar reasons as
247 // ProcessConnectDone().
248 PostDoRead();
249 }
250 return true;
251 }
252
253 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
254 // PostDoWrite())
255
Write(const char * data,size_t len)256 bool ChromeAsyncSocket::Write(const char* data, size_t len) {
257 if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) {
258 LOG(DFATAL) << "Write() called on non-open non-tls-connecting socket";
259 DoNonNetError(ERROR_WRONGSTATE);
260 return false;
261 }
262 // TODO(akalin): Avoid this check by modifying the interface to have
263 // a "ready for writing" signal.
264 if ((static_cast<size_t>(write_buf_->size()) - write_end_) < len) {
265 LOG(DFATAL) << "queueing " << len << " bytes would exceed the "
266 << "max write buffer size = " << write_buf_->size()
267 << " by " << (len - write_buf_->size()) << " bytes";
268 DoNetError(net::ERR_INSUFFICIENT_RESOURCES);
269 return false;
270 }
271 std::memcpy(write_buf_->data() + write_end_, data, len);
272 write_end_ += len;
273 // If we're TLS-connecting, the write buffer will get flushed once
274 // the TLS-connect finishes. Otherwise, start writing if we're not
275 // already writing and we have something to write.
276 if ((state_ != STATE_TLS_CONNECTING) &&
277 (write_state_ == IDLE) && (write_end_ > 0U)) {
278 // We defer execution of DoWrite() here for similar reasons as
279 // ProcessConnectDone().
280 PostDoWrite();
281 }
282 return true;
283 }
284
285 // write_state_ == IDLE -> write_state_ == POSTED
286
PostDoWrite()287 void ChromeAsyncSocket::PostDoWrite() {
288 DCHECK(IsOpen());
289 DCHECK_EQ(write_state_, IDLE);
290 DCHECK_GT(write_end_, 0U);
291 base::MessageLoop* message_loop = base::MessageLoop::current();
292 CHECK(message_loop);
293 message_loop->PostTask(
294 FROM_HERE,
295 base::Bind(&ChromeAsyncSocket::DoWrite,
296 weak_ptr_factory_.GetWeakPtr()));
297 write_state_ = POSTED;
298 }
299
300 // write_state_ == POSTED -> write_state_ == PENDING
301
DoWrite()302 void ChromeAsyncSocket::DoWrite() {
303 DCHECK(IsOpen());
304 DCHECK_EQ(write_state_, POSTED);
305 DCHECK_GT(write_end_, 0U);
306 // Once we call Write(), we cannot call StartTls() until the write
307 // finishes. This is okay, as StartTls() is called only after we
308 // have received a reply to a message we sent to the server and
309 // before we send the next message.
310 int status =
311 transport_socket_->Write(
312 write_buf_.get(), write_end_,
313 base::Bind(&ChromeAsyncSocket::ProcessWriteDone,
314 weak_ptr_factory_.GetWeakPtr()));
315 write_state_ = PENDING;
316 if (status != net::ERR_IO_PENDING) {
317 ProcessWriteDone(status);
318 }
319 }
320
321 // write_state_ == PENDING -> write_state_ == IDLE or POSTED (the
322 // latter via PostDoWrite())
323
ProcessWriteDone(int status)324 void ChromeAsyncSocket::ProcessWriteDone(int status) {
325 DCHECK_NE(status, net::ERR_IO_PENDING);
326 DCHECK(IsOpen());
327 DCHECK_EQ(write_state_, PENDING);
328 DCHECK_GT(write_end_, 0U);
329 write_state_ = IDLE;
330 if (status < net::OK) {
331 DoNetErrorFromStatus(status);
332 DoClose();
333 return;
334 }
335 size_t written = static_cast<size_t>(status);
336 if (written > write_end_) {
337 LOG(DFATAL) << "bytes written = " << written
338 << " exceeds bytes requested = " << write_end_;
339 DoNetError(net::ERR_UNEXPECTED);
340 DoClose();
341 return;
342 }
343 // TODO(akalin): Figure out a better way to do this; perhaps a queue
344 // of DrainableIOBuffers. This'll also allow us to not have an
345 // artificial buffer size limit.
346 std::memmove(write_buf_->data(),
347 write_buf_->data() + written,
348 write_end_ - written);
349 write_end_ -= written;
350 if (write_end_ > 0U) {
351 PostDoWrite();
352 }
353 }
354
355 // * -> STATE_CLOSED
356
Close()357 bool ChromeAsyncSocket::Close() {
358 DoClose();
359 return true;
360 }
361
362 // (not STATE_CLOSED) -> STATE_CLOSED
363
DoClose()364 void ChromeAsyncSocket::DoClose() {
365 weak_ptr_factory_.InvalidateWeakPtrs();
366 if (transport_socket_.get()) {
367 transport_socket_->Disconnect();
368 }
369 transport_socket_.reset();
370 read_state_ = IDLE;
371 read_start_ = 0U;
372 read_end_ = 0U;
373 write_state_ = IDLE;
374 write_end_ = 0U;
375 if (state_ != STATE_CLOSED) {
376 state_ = STATE_CLOSED;
377 SignalClosed();
378 }
379 // Reset error variables after SignalClosed() so slots connected
380 // to it can read it.
381 error_ = ERROR_NONE;
382 net_error_ = net::OK;
383 }
384
385 // STATE_OPEN -> STATE_TLS_CONNECTING
386
StartTls(const std::string & domain_name)387 bool ChromeAsyncSocket::StartTls(const std::string& domain_name) {
388 if ((state_ != STATE_OPEN) || (read_state_ == PENDING) ||
389 (write_state_ != IDLE)) {
390 LOG(DFATAL) << "StartTls() called in wrong state";
391 DoNonNetError(ERROR_WRONGSTATE);
392 return false;
393 }
394
395 state_ = STATE_TLS_CONNECTING;
396 read_state_ = IDLE;
397 read_start_ = 0U;
398 read_end_ = 0U;
399 DCHECK_EQ(write_end_, 0U);
400
401 // Clear out any posted DoRead() tasks.
402 weak_ptr_factory_.InvalidateWeakPtrs();
403
404 DCHECK(transport_socket_.get());
405 scoped_ptr<net::ClientSocketHandle> socket_handle(
406 new net::ClientSocketHandle());
407 socket_handle->SetSocket(transport_socket_.Pass());
408 transport_socket_ =
409 resolving_client_socket_factory_->CreateSSLClientSocket(
410 socket_handle.Pass(), net::HostPortPair(domain_name, 443));
411 int status = transport_socket_->Connect(
412 base::Bind(&ChromeAsyncSocket::ProcessSSLConnectDone,
413 weak_ptr_factory_.GetWeakPtr()));
414 if (status != net::ERR_IO_PENDING) {
415 base::MessageLoop* message_loop = base::MessageLoop::current();
416 CHECK(message_loop);
417 message_loop->PostTask(
418 FROM_HERE,
419 base::Bind(&ChromeAsyncSocket::ProcessSSLConnectDone,
420 weak_ptr_factory_.GetWeakPtr(), status));
421 }
422 return true;
423 }
424
425 // STATE_TLS_CONNECTING -> STATE_TLS_OPEN
426 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
427 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
428 // PostDoWrite())
429
ProcessSSLConnectDone(int status)430 void ChromeAsyncSocket::ProcessSSLConnectDone(int status) {
431 DCHECK_NE(status, net::ERR_IO_PENDING);
432 DCHECK_EQ(state_, STATE_TLS_CONNECTING);
433 DCHECK_EQ(read_state_, IDLE);
434 DCHECK_EQ(read_start_, 0U);
435 DCHECK_EQ(read_end_, 0U);
436 DCHECK_EQ(write_state_, IDLE);
437 if (status != net::OK) {
438 DoNetErrorFromStatus(status);
439 DoClose();
440 return;
441 }
442 state_ = STATE_TLS_OPEN;
443 PostDoRead();
444 if (write_end_ > 0U) {
445 PostDoWrite();
446 }
447 SignalSSLConnected();
448 }
449
450 } // namespace jingle_glue
451