1 // Copyright (c) 2006-2008 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 "net/socket/tcp_client_socket_libevent.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <netdb.h>
10 #include <sys/socket.h>
11 #include <netinet/tcp.h>
12
13 #include "base/eintr_wrapper.h"
14 #include "base/message_loop.h"
15 #include "base/string_util.h"
16 #include "base/trace_event.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/load_log.h"
19 #include "net/base/net_errors.h"
20 #if defined(USE_SYSTEM_LIBEVENT)
21 #include <event.h>
22 #else
23 #include "third_party/libevent/event.h"
24 #endif
25
26 namespace net {
27
28 namespace {
29
30 const int kInvalidSocket = -1;
31
32 // Return 0 on success, -1 on failure.
33 // Too small a function to bother putting in a library?
SetNonBlocking(int fd)34 int SetNonBlocking(int fd) {
35 int flags = fcntl(fd, F_GETFL, 0);
36 if (-1 == flags)
37 return flags;
38 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
39 }
40
41 // DisableNagle turns off buffering in the kernel. By default, TCP sockets will
42 // wait up to 200ms for more data to complete a packet before transmitting.
43 // After calling this function, the kernel will not wait. See TCP_NODELAY in
44 // `man 7 tcp`.
DisableNagle(int fd)45 int DisableNagle(int fd) {
46 int on = 1;
47 return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
48 }
49
50 // Convert values from <errno.h> to values from "net/base/net_errors.h"
MapPosixError(int os_error)51 int MapPosixError(int os_error) {
52 // There are numerous posix error codes, but these are the ones we thus far
53 // find interesting.
54 switch (os_error) {
55 case EAGAIN:
56 #if EWOULDBLOCK != EAGAIN
57 case EWOULDBLOCK:
58 #endif
59 return ERR_IO_PENDING;
60 case EACCES:
61 return ERR_ACCESS_DENIED;
62 case ENETDOWN:
63 return ERR_INTERNET_DISCONNECTED;
64 case ETIMEDOUT:
65 return ERR_TIMED_OUT;
66 case ECONNRESET:
67 case ENETRESET: // Related to keep-alive
68 case EPIPE:
69 return ERR_CONNECTION_RESET;
70 case ECONNABORTED:
71 return ERR_CONNECTION_ABORTED;
72 case ECONNREFUSED:
73 return ERR_CONNECTION_REFUSED;
74 case EHOSTUNREACH:
75 case ENETUNREACH:
76 return ERR_ADDRESS_UNREACHABLE;
77 case EADDRNOTAVAIL:
78 return ERR_ADDRESS_INVALID;
79 case 0:
80 return OK;
81 default:
82 LOG(WARNING) << "Unknown error " << os_error
83 << " mapped to net::ERR_FAILED";
84 return ERR_FAILED;
85 }
86 }
87
MapConnectError(int os_error)88 int MapConnectError(int os_error) {
89 switch (os_error) {
90 case ETIMEDOUT:
91 return ERR_CONNECTION_TIMED_OUT;
92 default: {
93 int net_error = MapPosixError(os_error);
94 if (net_error == ERR_FAILED)
95 return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED.
96 return net_error;
97 }
98 }
99 }
100
101 // Given os_error, an errno from a connect() attempt, returns true if
102 // connect() should be retried with another address.
ShouldTryNextAddress(int os_error)103 bool ShouldTryNextAddress(int os_error) {
104 switch (os_error) {
105 case EADDRNOTAVAIL:
106 case EAFNOSUPPORT:
107 case ECONNREFUSED:
108 case ECONNRESET:
109 case EACCES:
110 case EPERM:
111 case ENETUNREACH:
112 case EHOSTUNREACH:
113 case ENETDOWN:
114 case ETIMEDOUT:
115 return true;
116 default:
117 return false;
118 }
119 }
120
121 } // namespace
122
123 //-----------------------------------------------------------------------------
124
TCPClientSocketLibevent(const AddressList & addresses)125 TCPClientSocketLibevent::TCPClientSocketLibevent(const AddressList& addresses)
126 : socket_(kInvalidSocket),
127 addresses_(addresses),
128 current_ai_(addresses_.head()),
129 waiting_connect_(false),
130 read_watcher_(this),
131 write_watcher_(this),
132 read_callback_(NULL),
133 write_callback_(NULL) {
134 }
135
~TCPClientSocketLibevent()136 TCPClientSocketLibevent::~TCPClientSocketLibevent() {
137 Disconnect();
138 }
139
Connect(CompletionCallback * callback,LoadLog * load_log)140 int TCPClientSocketLibevent::Connect(CompletionCallback* callback,
141 LoadLog* load_log) {
142 // If already connected, then just return OK.
143 if (socket_ != kInvalidSocket)
144 return OK;
145
146 DCHECK(!waiting_connect_);
147 DCHECK(!load_log_);
148
149 TRACE_EVENT_BEGIN("socket.connect", this, "");
150
151 LoadLog::BeginEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
152
153 int rv = DoConnect();
154
155 if (rv == ERR_IO_PENDING) {
156 // Synchronous operation not supported.
157 DCHECK(callback);
158
159 load_log_ = load_log;
160 waiting_connect_ = true;
161 write_callback_ = callback;
162 } else {
163 TRACE_EVENT_END("socket.connect", this, "");
164 LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
165 }
166
167 return rv;
168 }
169
DoConnect()170 int TCPClientSocketLibevent::DoConnect() {
171 while (true) {
172 DCHECK(current_ai_);
173
174 int rv = CreateSocket(current_ai_);
175 if (rv != OK)
176 return rv;
177
178 if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr,
179 static_cast<int>(current_ai_->ai_addrlen)))) {
180 // Connected without waiting!
181 return OK;
182 }
183
184 int os_error = errno;
185 if (os_error == EINPROGRESS)
186 break;
187
188 close(socket_);
189 socket_ = kInvalidSocket;
190
191 if (current_ai_->ai_next && ShouldTryNextAddress(os_error)) {
192 // connect() can fail synchronously for an address even on a
193 // non-blocking socket. As an example, this can happen when there is
194 // no route to the host. Retry using the next address in the list.
195 current_ai_ = current_ai_->ai_next;
196 } else {
197 DLOG(INFO) << "connect failed: " << os_error;
198 return MapConnectError(os_error);
199 }
200 }
201
202 // Initialize write_socket_watcher_ and link it to our MessagePump.
203 // POLLOUT is set if the connection is established.
204 // POLLIN is set if the connection fails.
205 if (!MessageLoopForIO::current()->WatchFileDescriptor(
206 socket_, true, MessageLoopForIO::WATCH_WRITE, &write_socket_watcher_,
207 &write_watcher_)) {
208 DLOG(INFO) << "WatchFileDescriptor failed: " << errno;
209 close(socket_);
210 socket_ = kInvalidSocket;
211 return MapPosixError(errno);
212 }
213
214 return ERR_IO_PENDING;
215 }
216
Disconnect()217 void TCPClientSocketLibevent::Disconnect() {
218 if (socket_ == kInvalidSocket)
219 return;
220
221 TRACE_EVENT_INSTANT("socket.disconnect", this, "");
222
223 bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
224 DCHECK(ok);
225 ok = write_socket_watcher_.StopWatchingFileDescriptor();
226 DCHECK(ok);
227 close(socket_);
228 socket_ = kInvalidSocket;
229 waiting_connect_ = false;
230
231 // Reset for next time.
232 current_ai_ = addresses_.head();
233 }
234
IsConnected() const235 bool TCPClientSocketLibevent::IsConnected() const {
236 if (socket_ == kInvalidSocket || waiting_connect_)
237 return false;
238
239 // Check if connection is alive.
240 char c;
241 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
242 if (rv == 0)
243 return false;
244 if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
245 return false;
246
247 return true;
248 }
249
IsConnectedAndIdle() const250 bool TCPClientSocketLibevent::IsConnectedAndIdle() const {
251 if (socket_ == kInvalidSocket || waiting_connect_)
252 return false;
253
254 // Check if connection is alive and we haven't received any data
255 // unexpectedly.
256 char c;
257 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
258 if (rv >= 0)
259 return false;
260 if (errno != EAGAIN && errno != EWOULDBLOCK)
261 return false;
262
263 return true;
264 }
265
Read(IOBuffer * buf,int buf_len,CompletionCallback * callback)266 int TCPClientSocketLibevent::Read(IOBuffer* buf,
267 int buf_len,
268 CompletionCallback* callback) {
269 DCHECK_NE(kInvalidSocket, socket_);
270 DCHECK(!waiting_connect_);
271 DCHECK(!read_callback_);
272 // Synchronous operation not supported
273 DCHECK(callback);
274 DCHECK_GT(buf_len, 0);
275
276 TRACE_EVENT_BEGIN("socket.read", this, "");
277 int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len));
278 if (nread >= 0) {
279 TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", nread));
280 return nread;
281 }
282 if (errno != EAGAIN && errno != EWOULDBLOCK) {
283 DLOG(INFO) << "read failed, errno " << errno;
284 return MapPosixError(errno);
285 }
286
287 if (!MessageLoopForIO::current()->WatchFileDescriptor(
288 socket_, true, MessageLoopForIO::WATCH_READ,
289 &read_socket_watcher_, &read_watcher_)) {
290 DLOG(INFO) << "WatchFileDescriptor failed on read, errno " << errno;
291 return MapPosixError(errno);
292 }
293
294 read_buf_ = buf;
295 read_buf_len_ = buf_len;
296 read_callback_ = callback;
297 return ERR_IO_PENDING;
298 }
299
Write(IOBuffer * buf,int buf_len,CompletionCallback * callback)300 int TCPClientSocketLibevent::Write(IOBuffer* buf,
301 int buf_len,
302 CompletionCallback* callback) {
303 DCHECK_NE(kInvalidSocket, socket_);
304 DCHECK(!waiting_connect_);
305 DCHECK(!write_callback_);
306 // Synchronous operation not supported
307 DCHECK(callback);
308 DCHECK_GT(buf_len, 0);
309
310 TRACE_EVENT_BEGIN("socket.write", this, "");
311 int nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len));
312 if (nwrite >= 0) {
313 TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", nwrite));
314 return nwrite;
315 }
316 if (errno != EAGAIN && errno != EWOULDBLOCK)
317 return MapPosixError(errno);
318
319 if (!MessageLoopForIO::current()->WatchFileDescriptor(
320 socket_, true, MessageLoopForIO::WATCH_WRITE,
321 &write_socket_watcher_, &write_watcher_)) {
322 DLOG(INFO) << "WatchFileDescriptor failed on write, errno " << errno;
323 return MapPosixError(errno);
324 }
325
326
327 write_buf_ = buf;
328 write_buf_len_ = buf_len;
329 write_callback_ = callback;
330 return ERR_IO_PENDING;
331 }
332
SetReceiveBufferSize(int32 size)333 bool TCPClientSocketLibevent::SetReceiveBufferSize(int32 size) {
334 int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
335 reinterpret_cast<const char*>(&size),
336 sizeof(size));
337 DCHECK(!rv) << "Could not set socket receive buffer size: " << errno;
338 return rv == 0;
339 }
340
SetSendBufferSize(int32 size)341 bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) {
342 int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
343 reinterpret_cast<const char*>(&size),
344 sizeof(size));
345 DCHECK(!rv) << "Could not set socket send buffer size: " << errno;
346 return rv == 0;
347 }
348
349
CreateSocket(const addrinfo * ai)350 int TCPClientSocketLibevent::CreateSocket(const addrinfo* ai) {
351 socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
352 if (socket_ == kInvalidSocket)
353 return MapPosixError(errno);
354
355 if (SetNonBlocking(socket_)) {
356 const int err = MapPosixError(errno);
357 close(socket_);
358 socket_ = kInvalidSocket;
359 return err;
360 }
361
362 // This mirrors the behaviour on Windows. See the comment in
363 // tcp_client_socket_win.cc after searching for "NODELAY".
364 DisableNagle(socket_); // If DisableNagle fails, we don't care.
365
366 return OK;
367 }
368
DoReadCallback(int rv)369 void TCPClientSocketLibevent::DoReadCallback(int rv) {
370 DCHECK_NE(rv, ERR_IO_PENDING);
371 DCHECK(read_callback_);
372
373 // since Run may result in Read being called, clear read_callback_ up front.
374 CompletionCallback* c = read_callback_;
375 read_callback_ = NULL;
376 c->Run(rv);
377 }
378
DoWriteCallback(int rv)379 void TCPClientSocketLibevent::DoWriteCallback(int rv) {
380 DCHECK_NE(rv, ERR_IO_PENDING);
381 DCHECK(write_callback_);
382
383 // since Run may result in Write being called, clear write_callback_ up front.
384 CompletionCallback* c = write_callback_;
385 write_callback_ = NULL;
386 c->Run(rv);
387 }
388
DidCompleteConnect()389 void TCPClientSocketLibevent::DidCompleteConnect() {
390 int result = ERR_UNEXPECTED;
391
392 // Check to see if connect succeeded
393 int os_error = 0;
394 socklen_t len = sizeof(os_error);
395 if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0)
396 os_error = errno;
397
398 if (os_error == EINPROGRESS || os_error == EALREADY) {
399 NOTREACHED(); // This indicates a bug in libevent or our code.
400 result = ERR_IO_PENDING;
401 } else if (current_ai_->ai_next && ShouldTryNextAddress(os_error)) {
402 // This address failed, try next one in list.
403 const addrinfo* next = current_ai_->ai_next;
404 Disconnect();
405 current_ai_ = next;
406 scoped_refptr<LoadLog> load_log;
407 load_log.swap(load_log_);
408 TRACE_EVENT_END("socket.connect", this, "");
409 LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
410 result = Connect(write_callback_, load_log);
411 } else {
412 result = MapConnectError(os_error);
413 bool ok = write_socket_watcher_.StopWatchingFileDescriptor();
414 DCHECK(ok);
415 waiting_connect_ = false;
416 TRACE_EVENT_END("socket.connect", this, "");
417 LoadLog::EndEvent(load_log_, LoadLog::TYPE_TCP_CONNECT);
418 load_log_ = NULL;
419 }
420
421 if (result != ERR_IO_PENDING) {
422 DoWriteCallback(result);
423 }
424 }
425
DidCompleteRead()426 void TCPClientSocketLibevent::DidCompleteRead() {
427 int bytes_transferred;
428 bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(),
429 read_buf_len_));
430
431 int result;
432 if (bytes_transferred >= 0) {
433 TRACE_EVENT_END("socket.read", this,
434 StringPrintf("%d bytes", bytes_transferred));
435 result = bytes_transferred;
436 } else {
437 result = MapPosixError(errno);
438 }
439
440 if (result != ERR_IO_PENDING) {
441 read_buf_ = NULL;
442 read_buf_len_ = 0;
443 bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
444 DCHECK(ok);
445 DoReadCallback(result);
446 }
447 }
448
DidCompleteWrite()449 void TCPClientSocketLibevent::DidCompleteWrite() {
450 int bytes_transferred;
451 bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(),
452 write_buf_len_));
453
454 int result;
455 if (bytes_transferred >= 0) {
456 result = bytes_transferred;
457 TRACE_EVENT_END("socket.write", this,
458 StringPrintf("%d bytes", bytes_transferred));
459 } else {
460 result = MapPosixError(errno);
461 }
462
463 if (result != ERR_IO_PENDING) {
464 write_buf_ = NULL;
465 write_buf_len_ = 0;
466 write_socket_watcher_.StopWatchingFileDescriptor();
467 DoWriteCallback(result);
468 }
469 }
470
GetPeerName(struct sockaddr * name,socklen_t * namelen)471 int TCPClientSocketLibevent::GetPeerName(struct sockaddr* name,
472 socklen_t* namelen) {
473 return ::getpeername(socket_, name, namelen);
474 }
475
476 } // namespace net
477