| Index: net/socket/unix_domain_server_socket_posix.cc
|
| diff --git a/net/socket/unix_domain_server_socket_posix.cc b/net/socket/unix_domain_server_socket_posix.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f9881d911f2a24d9e7c332d5229cc10652f8ee72
|
| --- /dev/null
|
| +++ b/net/socket/unix_domain_server_socket_posix.cc
|
| @@ -0,0 +1,190 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "net/socket/unix_domain_server_socket_posix.h"
|
| +
|
| +#include <errno.h>
|
| +#include <sys/socket.h>
|
| +#include <sys/un.h>
|
| +#include <unistd.h>
|
| +
|
| +#include "base/callback.h"
|
| +#include "base/callback_helpers.h"
|
| +#include "base/posix/eintr_wrapper.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/socket/unix_domain_client_socket_posix.h"
|
| +
|
| +namespace net {
|
| +
|
| +UnixDomainServerSocket::UnixDomainServerSocket(
|
| + const AuthCallback& auth_callback,
|
| + bool use_abstract_namespace)
|
| + : socket_fd_(kInvalidSocket),
|
| + auth_callback_(auth_callback),
|
| + use_abstract_namespace_(use_abstract_namespace) {
|
| +}
|
| +
|
| +UnixDomainServerSocket::~UnixDomainServerSocket() {
|
| + Close();
|
| +}
|
| +
|
| +// static
|
| +int UnixDomainServerSocket::CreateAndBind(const std::string& socket_path,
|
| + bool use_abstract_namespace,
|
| + SocketDescriptor* socket_fd) {
|
| + sockaddr_un addr;
|
| + socklen_t addr_len = sizeof(addr);
|
| + if (!UnixDomainClientSocket::FillAddress(socket_path,
|
| + use_abstract_namespace,
|
| + &addr, &addr_len))
|
| + return ERR_ADDRESS_INVALID;
|
| +
|
| + SocketDescriptor s = CreatePlatformSocket(PF_UNIX, SOCK_STREAM, 0);
|
| + if (s == kInvalidSocket)
|
| + return errno ? MapSystemError(errno) : ERR_UNEXPECTED;
|
| +
|
| + if (bind(s, reinterpret_cast<sockaddr*>(&addr), addr_len) < 0) {
|
| + int rv = MapSystemError(errno);
|
| + close(s);
|
| + PLOG(ERROR) << "Could not bind unix domain socket to " << socket_path
|
| + << (use_abstract_namespace ? " (with abstract namespace)" : "");
|
| + return rv;
|
| + }
|
| +
|
| + DCHECK(socket_fd);
|
| + *socket_fd = s;
|
| + return OK;
|
| +}
|
| +
|
| +// static
|
| +bool UnixDomainServerSocket::GetPeerIds(SocketDescriptor socket,
|
| + uid_t* user_id,
|
| + gid_t* group_id) {
|
| +#if defined(OS_LINUX) || defined(OS_ANDROID)
|
| + struct ucred user_cred;
|
| + socklen_t len = sizeof(user_cred);
|
| + if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) < 0)
|
| + return false;
|
| + *user_id = user_cred.uid;
|
| + *group_id = user_cred.gid;
|
| +#else
|
| + if (getpeereid(socket, user_id, group_id) < 0)
|
| + return false;
|
| +#endif
|
| + return true;
|
| +}
|
| +
|
| +void UnixDomainServerSocket::Close() {
|
| + if (socket_fd_ == kInvalidSocket)
|
| + return;
|
| + close(socket_fd_);
|
| + socket_fd_ = kInvalidSocket;
|
| +}
|
| +
|
| +int UnixDomainServerSocket::Listen(const IPEndPoint& address, int backlog) {
|
| + NOTIMPLEMENTED();
|
| + return ERR_NOT_IMPLEMENTED;
|
| +}
|
| +
|
| +int UnixDomainServerSocket::ListenWithAddressAndPort(
|
| + const std::string& unix_domain_path,
|
| + int port_unused,
|
| + int backlog) {
|
| + int rv = CreateAndBind(unix_domain_path,
|
| + use_abstract_namespace_,
|
| + &socket_fd_);
|
| + if (rv != OK)
|
| + return rv;
|
| +
|
| + if (listen(socket_fd_, backlog) < 0) {
|
| + PLOG(ERROR) << "listen() returned an error";
|
| + rv = MapSystemError(errno);
|
| + Close();
|
| + return rv;
|
| + }
|
| +
|
| + if (SetNonBlocking(socket_fd_)) {
|
| + rv = MapSystemError(errno);
|
| + Close();
|
| + return rv;
|
| + }
|
| +
|
| + return OK;
|
| +}
|
| +
|
| +int UnixDomainServerSocket::GetLocalAddress(IPEndPoint* address) const {
|
| + NOTIMPLEMENTED();
|
| + return ERR_NOT_IMPLEMENTED;
|
| +}
|
| +
|
| +int UnixDomainServerSocket::Accept(scoped_ptr<StreamSocket>* socket,
|
| + const CompletionCallback& callback) {
|
| + DCHECK(socket);
|
| + DCHECK(!callback.is_null());
|
| + DCHECK(accept_callback_.is_null());
|
| +
|
| + int rv = DoAccept(socket);
|
| + if (rv != ERR_IO_PENDING)
|
| + return rv;
|
| +
|
| + if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
|
| + socket_fd_, true, base::MessageLoopForIO::WATCH_READ,
|
| + &accept_socket_watcher_, this)) {
|
| + PLOG(ERROR) << "WatchFileDescriptor failed on read";
|
| + return MapSystemError(errno);
|
| + }
|
| +
|
| + accept_socket_ = socket;
|
| + accept_callback_ = callback;
|
| + return ERR_IO_PENDING;
|
| +}
|
| +
|
| +int UnixDomainServerSocket::DoAccept(scoped_ptr<StreamSocket>* socket) {
|
| + int new_socket = HANDLE_EINTR(accept(socket_fd_, NULL, NULL));
|
| + if (new_socket < 0) {
|
| + // Treat as IO_PENDING for ECONNABORTED. See MapAcceptError() in
|
| + // tcp_socket_libevent.cc for detailed reason.
|
| + return errno == ECONNABORTED ? ERR_IO_PENDING : MapSystemError(errno);
|
| + }
|
| +
|
| + uid_t user_id;
|
| + gid_t group_id;
|
| + if (!GetPeerIds(new_socket, &user_id, &group_id) ||
|
| + !auth_callback_.Run(user_id, group_id)) {
|
| + close(new_socket);
|
| + return ERR_IO_PENDING;
|
| + }
|
| +
|
| + if (SetNonBlocking(new_socket)) {
|
| + close(new_socket);
|
| + return ERR_IO_PENDING;
|
| + }
|
| +
|
| + socket->reset(new UnixDomainClientSocket(new_socket));
|
| + return OK;
|
| +}
|
| +
|
| +void UnixDomainServerSocket::DidCompleteAccept() {
|
| + int rv = DoAccept(accept_socket_);
|
| + if (rv == ERR_IO_PENDING)
|
| + return;
|
| +
|
| + accept_socket_watcher_.StopWatchingFileDescriptor();
|
| + accept_socket_ = NULL;
|
| + base::ResetAndReturn(&accept_callback_).Run(rv);
|
| +}
|
| +
|
| +void UnixDomainServerSocket::OnFileCanReadWithoutBlocking(int fd) {
|
| + if (accept_callback_.is_null()) {
|
| + NOTREACHED();
|
| + } else {
|
| + DidCompleteAccept();
|
| + }
|
| +}
|
| +
|
| +void UnixDomainServerSocket::OnFileCanWriteWithoutBlocking(int fd) {
|
| + NOTREACHED();
|
| +}
|
| +
|
| +} // namespace net
|
|
|