blob: b2729a6145721796a08967a93c7d279cf4f7ea10 [file] [log] [blame]
Chris Masone8a7d06b2014-10-24 14:47:12 -07001// Copyright 2014 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
James Robinsonb4b7af22014-12-05 11:21:01 -08005#include "shell/domain_socket/socket_libevent.h"
Chris Masone8a7d06b2014-10-24 14:47:12 -07006
7#include <errno.h>
8#include <fcntl.h>
9#include <netinet/in.h>
10#include <sys/socket.h>
11#include <unistd.h>
12
13#include "base/callback_helpers.h"
14#include "base/logging.h"
15#include "base/posix/eintr_wrapper.h"
James Robinsonb4b7af22014-12-05 11:21:01 -080016#include "shell/domain_socket/net_errors.h"
Chris Masone8a7d06b2014-10-24 14:47:12 -070017
18namespace mojo {
19namespace shell {
20
21SockaddrStorage::SockaddrStorage(const SockaddrStorage& other)
22 : addr_len(other.addr_len),
23 addr(reinterpret_cast<struct sockaddr*>(&addr_storage)) {
24 memcpy(addr, other.addr, addr_len);
25}
26
27void SockaddrStorage::operator=(const SockaddrStorage& other) {
28 addr_len = other.addr_len;
29 // addr is already set to &this->addr_storage by default ctor.
30 memcpy(addr, other.addr, addr_len);
31}
32
33namespace {
34
35int MapAcceptError(int os_error) {
36 switch (os_error) {
37 // If the client aborts the connection before the server calls accept,
38 // POSIX specifies accept should fail with ECONNABORTED. The server can
39 // ignore the error and just call accept again, so we map the error to
40 // ERR_IO_PENDING. See UNIX Network Programming, Vol. 1, 3rd Ed., Sec.
41 // 5.11, "Connection Abort before accept Returns".
42 case ECONNABORTED:
43 return net::ERR_IO_PENDING;
44 default:
45 return net::MapSystemError(os_error);
46 }
47}
48
49int MapConnectError(int os_error) {
50 switch (os_error) {
51 case EINPROGRESS:
52 return net::ERR_IO_PENDING;
53 case EACCES:
54 return net::ERR_NETWORK_ACCESS_DENIED;
55 case ETIMEDOUT:
56 return net::ERR_CONNECTION_TIMED_OUT;
57 default: {
58 int net_error = net::MapSystemError(os_error);
59 if (net_error == net::ERR_FAILED)
60 return net::ERR_CONNECTION_FAILED; // More specific than ERR_FAILED.
61 return net_error;
62 }
63 }
64}
65
James Robinsond15579e2014-10-27 11:04:06 -070066int SetNonBlocking(int fd) {
67 int flags = fcntl(fd, F_GETFL, 0);
68 if (-1 == flags)
69 return flags;
70 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
71}
72
Chris Masone8a7d06b2014-10-24 14:47:12 -070073} // namespace
74
75SocketLibevent::SocketLibevent()
76 : socket_fd_(kInvalidSocket), waiting_connect_(false) {
77}
78
79SocketLibevent::~SocketLibevent() {
80 Close();
81}
82
83int SocketLibevent::Open(int address_family) {
84 DCHECK(thread_checker_.CalledOnValidThread());
85 DCHECK_EQ(kInvalidSocket, socket_fd_);
86 DCHECK(address_family == AF_INET || address_family == AF_INET6 ||
87 address_family == AF_UNIX);
88
James Robinsond15579e2014-10-27 11:04:06 -070089 int socket_type = SOCK_STREAM;
90#ifdef SOCK_NONBLOCK
91 socket_type |= SOCK_NONBLOCK;
92#endif
James Robinsonb4b7af22014-12-05 11:21:01 -080093 socket_fd_ = ::socket(address_family, socket_type,
Chris Masone8a7d06b2014-10-24 14:47:12 -070094 address_family == AF_UNIX ? 0 : IPPROTO_TCP);
James Robinsond15579e2014-10-27 11:04:06 -070095#ifndef SOCK_NONBLOCK
96 if (SetNonBlocking(socket_fd_) != 0) {
97 PLOG(ERROR) << "SetNonBlocking() returned an error, errno=" << errno;
98 return net::MapSystemError(errno);
99 }
100#endif
Chris Masone8a7d06b2014-10-24 14:47:12 -0700101 if (socket_fd_ < 0) {
102 PLOG(ERROR) << "CreatePlatformSocket() returned an error, errno=" << errno;
103 return net::MapSystemError(errno);
104 }
105
106 return net::OK;
107}
108
Chris Masone8a7d06b2014-10-24 14:47:12 -0700109int SocketLibevent::AdoptConnectedSocket(SocketDescriptor socket,
110 const SockaddrStorage& address) {
111 DCHECK(thread_checker_.CalledOnValidThread());
112 DCHECK_EQ(kInvalidSocket, socket_fd_);
113
114 socket_fd_ = socket;
115
116 if (SetNonBlocking(socket_fd_)) {
117 int rv = net::MapSystemError(errno);
118 Close();
119 return rv;
120 }
121
122 SetPeerAddress(address);
123 return net::OK;
124}
125
126SocketDescriptor SocketLibevent::ReleaseConnectedSocket() {
127 StopWatchingAndCleanUp();
128 SocketDescriptor socket_fd = socket_fd_;
129 socket_fd_ = kInvalidSocket;
130 return socket_fd;
131}
132
133int SocketLibevent::Bind(const SockaddrStorage& address) {
134 DCHECK(thread_checker_.CalledOnValidThread());
135 DCHECK_NE(kInvalidSocket, socket_fd_);
136
137 int rv = bind(socket_fd_, address.addr, address.addr_len);
138 if (rv < 0) {
139 PLOG(ERROR) << "bind() returned an error, errno=" << errno;
140 return net::MapSystemError(errno);
141 }
142
143 return net::OK;
144}
145
146int SocketLibevent::Listen(int backlog) {
147 DCHECK(thread_checker_.CalledOnValidThread());
148 DCHECK_NE(kInvalidSocket, socket_fd_);
149 DCHECK_LT(0, backlog);
150
151 int rv = listen(socket_fd_, backlog);
152 if (rv < 0) {
153 PLOG(ERROR) << "listen() returned an error, errno=" << errno;
154 return net::MapSystemError(errno);
155 }
156
157 return net::OK;
158}
159
160int SocketLibevent::Accept(scoped_ptr<SocketLibevent>* socket,
161 const CompletionCallback& callback) {
162 DCHECK(thread_checker_.CalledOnValidThread());
163 DCHECK_NE(kInvalidSocket, socket_fd_);
164 DCHECK(accept_callback_.is_null());
165 DCHECK(socket);
166 DCHECK(!callback.is_null());
167
168 int rv = DoAccept(socket);
169 if (rv != net::ERR_IO_PENDING)
170 return rv;
171
172 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
James Robinsonb4b7af22014-12-05 11:21:01 -0800173 socket_fd_, true, base::MessageLoopForIO::WATCH_READ,
174 &accept_socket_watcher_, this)) {
Chris Masone8a7d06b2014-10-24 14:47:12 -0700175 PLOG(ERROR) << "WatchFileDescriptor failed on accept, errno " << errno;
176 return net::MapSystemError(errno);
177 }
178
179 accept_socket_ = socket;
180 accept_callback_ = callback;
181 return net::ERR_IO_PENDING;
182}
183
184int SocketLibevent::Connect(const SockaddrStorage& address,
185 const CompletionCallback& callback) {
186 DCHECK(thread_checker_.CalledOnValidThread());
187 DCHECK_NE(kInvalidSocket, socket_fd_);
188 DCHECK(!waiting_connect_);
189 DCHECK(!callback.is_null());
190
191 SetPeerAddress(address);
192
193 int rv = DoConnect();
194 if (rv != net::ERR_IO_PENDING)
195 return rv;
196
197 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
James Robinsonb4b7af22014-12-05 11:21:01 -0800198 socket_fd_, true, base::MessageLoopForIO::WATCH_WRITE,
199 &write_socket_watcher_, this)) {
Chris Masone8a7d06b2014-10-24 14:47:12 -0700200 PLOG(ERROR) << "WatchFileDescriptor failed on connect, errno " << errno;
201 return net::MapSystemError(errno);
202 }
203
204 write_callback_ = callback;
205 waiting_connect_ = true;
206 return net::ERR_IO_PENDING;
207}
208
209bool SocketLibevent::IsConnected() const {
210 DCHECK(thread_checker_.CalledOnValidThread());
211
212 if (socket_fd_ == kInvalidSocket || waiting_connect_)
213 return false;
214
215 // Checks if connection is alive.
216 char c;
217 int rv = HANDLE_EINTR(recv(socket_fd_, &c, 1, MSG_PEEK));
218 if (rv == 0)
219 return false;
220 if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
221 return false;
222
223 return true;
224}
225
226bool SocketLibevent::IsConnectedAndIdle() const {
227 DCHECK(thread_checker_.CalledOnValidThread());
228
229 if (socket_fd_ == kInvalidSocket || waiting_connect_)
230 return false;
231
232 // Check if connection is alive and we haven't received any data
233 // unexpectedly.
234 char c;
235 int rv = HANDLE_EINTR(recv(socket_fd_, &c, 1, MSG_PEEK));
236 if (rv >= 0)
237 return false;
238 if (errno != EAGAIN && errno != EWOULDBLOCK)
239 return false;
240
241 return true;
242}
243
244int SocketLibevent::GetLocalAddress(SockaddrStorage* address) const {
245 DCHECK(thread_checker_.CalledOnValidThread());
246 DCHECK(address);
247
248 if (getsockname(socket_fd_, address->addr, &address->addr_len) < 0)
249 return net::MapSystemError(errno);
250 return net::OK;
251}
252
253int SocketLibevent::GetPeerAddress(SockaddrStorage* address) const {
254 DCHECK(thread_checker_.CalledOnValidThread());
255 DCHECK(address);
256
257 if (!HasPeerAddress())
258 return net::ERR_SOCKET_NOT_CONNECTED;
259
260 *address = *peer_address_;
261 return net::OK;
262}
263
264void SocketLibevent::SetPeerAddress(const SockaddrStorage& address) {
265 DCHECK(thread_checker_.CalledOnValidThread());
Viet-Trung Luua417b142015-03-31 11:24:44 -0700266 // |peer_address_| will be non-null if Connect() has been called. Unless
Chris Masone8a7d06b2014-10-24 14:47:12 -0700267 // Close() is called to reset the internal state, a second call to Connect()
268 // is not allowed.
269 // Please note that we don't allow a second Connect() even if the previous
270 // Connect() has failed. Connecting the same |socket_| again after a
271 // connection attempt failed results in unspecified behavior according to
272 // POSIX.
273 DCHECK(!peer_address_);
274 peer_address_.reset(new SockaddrStorage(address));
275}
276
277bool SocketLibevent::HasPeerAddress() const {
278 DCHECK(thread_checker_.CalledOnValidThread());
Viet-Trung Luua417b142015-03-31 11:24:44 -0700279 return peer_address_ != nullptr;
Chris Masone8a7d06b2014-10-24 14:47:12 -0700280}
281
282void SocketLibevent::Close() {
283 DCHECK(thread_checker_.CalledOnValidThread());
284
285 StopWatchingAndCleanUp();
286
287 if (socket_fd_ != kInvalidSocket) {
288 if (IGNORE_EINTR(close(socket_fd_)) < 0)
289 PLOG(ERROR) << "close() returned an error, errno=" << errno;
290 socket_fd_ = kInvalidSocket;
291 }
292}
293
294void SocketLibevent::OnFileCanReadWithoutBlocking(int fd) {
295 DCHECK(!accept_callback_.is_null() || !read_callback_.is_null());
296 if (!accept_callback_.is_null()) {
297 AcceptCompleted();
298 } else { // !read_callback_.is_null()
299 NOTREACHED();
300 }
301}
302
303void SocketLibevent::OnFileCanWriteWithoutBlocking(int fd) {
304 DCHECK(!write_callback_.is_null());
305 if (waiting_connect_) {
306 ConnectCompleted();
307 } else {
308 NOTREACHED();
309 }
310}
311
312int SocketLibevent::DoAccept(scoped_ptr<SocketLibevent>* socket) {
313 SockaddrStorage new_peer_address;
314 int new_socket = HANDLE_EINTR(
315 accept(socket_fd_, new_peer_address.addr, &new_peer_address.addr_len));
316 if (new_socket < 0)
317 return MapAcceptError(errno);
318
319 scoped_ptr<SocketLibevent> accepted_socket(new SocketLibevent);
320 int rv = accepted_socket->AdoptConnectedSocket(new_socket, new_peer_address);
321 if (rv != net::OK)
322 return rv;
323
324 *socket = accepted_socket.Pass();
325 return net::OK;
326}
327
328void SocketLibevent::AcceptCompleted() {
329 DCHECK(accept_socket_);
330 int rv = DoAccept(accept_socket_);
331 if (rv == net::ERR_IO_PENDING)
332 return;
333
334 bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
335 DCHECK(ok);
Viet-Trung Luua417b142015-03-31 11:24:44 -0700336 accept_socket_ = nullptr;
Chris Masone8a7d06b2014-10-24 14:47:12 -0700337 base::ResetAndReturn(&accept_callback_).Run(rv);
338}
339
340int SocketLibevent::DoConnect() {
341 int rv = HANDLE_EINTR(
342 connect(socket_fd_, peer_address_->addr, peer_address_->addr_len));
343 DCHECK_GE(0, rv);
344 return rv == 0 ? net::OK : MapConnectError(errno);
345}
346
347void SocketLibevent::ConnectCompleted() {
348 // Get the error that connect() completed with.
349 int os_error = 0;
350 socklen_t len = sizeof(os_error);
351 if (getsockopt(socket_fd_, SOL_SOCKET, SO_ERROR, &os_error, &len) == 0) {
352 // TCPSocketLibevent expects errno to be set.
353 errno = os_error;
354 }
355
356 int rv = MapConnectError(errno);
357 if (rv == net::ERR_IO_PENDING)
358 return;
359
360 bool ok = write_socket_watcher_.StopWatchingFileDescriptor();
361 DCHECK(ok);
362 waiting_connect_ = false;
363 base::ResetAndReturn(&write_callback_).Run(rv);
364}
365
366void SocketLibevent::StopWatchingAndCleanUp() {
367 bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
368 DCHECK(ok);
369
370 if (!accept_callback_.is_null()) {
Viet-Trung Luua417b142015-03-31 11:24:44 -0700371 accept_socket_ = nullptr;
Chris Masone8a7d06b2014-10-24 14:47:12 -0700372 accept_callback_.Reset();
373 }
374
375 waiting_connect_ = false;
376 peer_address_.reset();
377}
378
379} // namespace shell
380} // namespace mojo