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 "net/socket/tcp_server_socket_libevent.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <netdb.h>
10#include <sys/socket.h>
11
12#include "build/build_config.h"
13
14#if defined(OS_POSIX)
15#include <netinet/in.h>
16#endif
17
18#include "base/posix/eintr_wrapper.h"
19#include "net/base/ip_endpoint.h"
20#include "net/base/net_errors.h"
21#include "net/base/net_util.h"
22#include "net/socket/socket_net_log_params.h"
23#include "net/socket/tcp_client_socket.h"
24
25namespace net {
26
27namespace {
28
29const int kInvalidSocket = -1;
30
31}  // namespace
32
33TCPServerSocketLibevent::TCPServerSocketLibevent(
34    net::NetLog* net_log,
35    const net::NetLog::Source& source)
36    : socket_(kInvalidSocket),
37      accept_socket_(NULL),
38      net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) {
39  net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
40                      source.ToEventParametersCallback());
41}
42
43TCPServerSocketLibevent::~TCPServerSocketLibevent() {
44  if (socket_ != kInvalidSocket)
45    Close();
46  net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
47}
48
49int TCPServerSocketLibevent::Listen(const IPEndPoint& address, int backlog) {
50  DCHECK(CalledOnValidThread());
51  DCHECK_GT(backlog, 0);
52  DCHECK_EQ(socket_, kInvalidSocket);
53
54  socket_ = socket(address.GetSockAddrFamily(), SOCK_STREAM, IPPROTO_TCP);
55  if (socket_ < 0) {
56    PLOG(ERROR) << "socket() returned an error";
57    return MapSystemError(errno);
58  }
59
60  if (SetNonBlocking(socket_)) {
61    int result = MapSystemError(errno);
62    Close();
63    return result;
64  }
65
66  int result = SetSocketOptions();
67  if (result != OK) {
68    Close();
69    return result;
70  }
71
72  SockaddrStorage storage;
73  if (!address.ToSockAddr(storage.addr, &storage.addr_len)) {
74    Close();
75    return ERR_ADDRESS_INVALID;
76  }
77
78  result = bind(socket_, storage.addr, storage.addr_len);
79  if (result < 0) {
80    PLOG(ERROR) << "bind() returned an error";
81    result = MapSystemError(errno);
82    Close();
83    return result;
84  }
85
86  result = listen(socket_, backlog);
87  if (result < 0) {
88    PLOG(ERROR) << "listen() returned an error";
89    result = MapSystemError(errno);
90    Close();
91    return result;
92  }
93
94  return OK;
95}
96
97int TCPServerSocketLibevent::GetLocalAddress(IPEndPoint* address) const {
98  DCHECK(CalledOnValidThread());
99  DCHECK(address);
100
101  SockaddrStorage storage;
102  if (getsockname(socket_, storage.addr, &storage.addr_len) < 0)
103    return MapSystemError(errno);
104  if (!address->FromSockAddr(storage.addr, storage.addr_len))
105    return ERR_FAILED;
106
107  return OK;
108}
109
110int TCPServerSocketLibevent::Accept(
111    scoped_ptr<StreamSocket>* socket, const CompletionCallback& callback) {
112  DCHECK(CalledOnValidThread());
113  DCHECK(socket);
114  DCHECK(!callback.is_null());
115  DCHECK(accept_callback_.is_null());
116
117  net_log_.BeginEvent(NetLog::TYPE_TCP_ACCEPT);
118
119  int result = AcceptInternal(socket);
120
121  if (result == ERR_IO_PENDING) {
122    if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
123            socket_, true, base::MessageLoopForIO::WATCH_READ,
124            &accept_socket_watcher_, this)) {
125      PLOG(ERROR) << "WatchFileDescriptor failed on read";
126      return MapSystemError(errno);
127    }
128
129    accept_socket_ = socket;
130    accept_callback_ = callback;
131  }
132
133  return result;
134}
135
136int TCPServerSocketLibevent::SetSocketOptions() {
137  // SO_REUSEADDR is useful for server sockets to bind to a recently unbound
138  // port. When a socket is closed, the end point changes its state to TIME_WAIT
139  // and wait for 2 MSL (maximum segment lifetime) to ensure the remote peer
140  // acknowledges its closure. For server sockets, it is usually safe to
141  // bind to a TIME_WAIT end point immediately, which is a widely adopted
142  // behavior.
143  //
144  // Note that on *nix, SO_REUSEADDR does not enable the TCP socket to bind to
145  // an end point that is already bound by another socket. To do that one must
146  // set SO_REUSEPORT instead. This option is not provided on Linux prior
147  // to 3.9.
148  //
149  // SO_REUSEPORT is provided in MacOS X and iOS.
150  int true_value = 1;
151  int rv = setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &true_value,
152                      sizeof(true_value));
153  if (rv < 0)
154    return MapSystemError(errno);
155  return OK;
156}
157
158int TCPServerSocketLibevent::AcceptInternal(
159    scoped_ptr<StreamSocket>* socket) {
160  SockaddrStorage storage;
161  int new_socket = HANDLE_EINTR(accept(socket_,
162                                       storage.addr,
163                                       &storage.addr_len));
164  if (new_socket < 0) {
165    int net_error = MapSystemError(errno);
166    if (net_error != ERR_IO_PENDING)
167      net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, net_error);
168    return net_error;
169  }
170
171  IPEndPoint address;
172  if (!address.FromSockAddr(storage.addr, storage.addr_len)) {
173    NOTREACHED();
174    if (HANDLE_EINTR(close(new_socket)) < 0)
175      PLOG(ERROR) << "close";
176    net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, ERR_FAILED);
177    return ERR_FAILED;
178  }
179  scoped_ptr<TCPClientSocket> tcp_socket(new TCPClientSocket(
180      AddressList(address),
181      net_log_.net_log(), net_log_.source()));
182  int adopt_result = tcp_socket->AdoptSocket(new_socket);
183  if (adopt_result != OK) {
184    if (HANDLE_EINTR(close(new_socket)) < 0)
185      PLOG(ERROR) << "close";
186    net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, adopt_result);
187    return adopt_result;
188  }
189  socket->reset(tcp_socket.release());
190  net_log_.EndEvent(NetLog::TYPE_TCP_ACCEPT,
191                    CreateNetLogIPEndPointCallback(&address));
192  return OK;
193}
194
195void TCPServerSocketLibevent::Close() {
196  if (socket_ != kInvalidSocket) {
197    bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
198    DCHECK(ok);
199    if (HANDLE_EINTR(close(socket_)) < 0)
200      PLOG(ERROR) << "close";
201    socket_ = kInvalidSocket;
202  }
203}
204
205void TCPServerSocketLibevent::OnFileCanReadWithoutBlocking(int fd) {
206  DCHECK(CalledOnValidThread());
207
208  int result = AcceptInternal(accept_socket_);
209  if (result != ERR_IO_PENDING) {
210    accept_socket_ = NULL;
211    bool ok = accept_socket_watcher_.StopWatchingFileDescriptor();
212    DCHECK(ok);
213    CompletionCallback callback = accept_callback_;
214    accept_callback_.Reset();
215    callback.Run(result);
216  }
217}
218
219void TCPServerSocketLibevent::OnFileCanWriteWithoutBlocking(int fd) {
220  NOTREACHED();
221}
222
223}  // namespace net
224