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