blob: ecd9adf3d3603e53e7fc5eb73cd4a29f7fbbf794 [file] [log] [blame]
// 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 <utility>
#include "base/logging.h"
#include "net/base/net_errors.h"
#include "net/base/sockaddr_storage.h"
#include "net/socket/socket_posix.h"
#include "net/socket/unix_domain_client_socket_posix.h"
namespace net {
namespace {
// Intended for use as SetterCallbacks in Accept() helper methods.
void SetStreamSocket(std::unique_ptr<StreamSocket>* socket,
std::unique_ptr<SocketPosix> accepted_socket) {
socket->reset(new UnixDomainClientSocket(std::move(accepted_socket)));
}
void SetSocketDescriptor(SocketDescriptor* socket,
std::unique_ptr<SocketPosix> accepted_socket) {
*socket = accepted_socket->ReleaseConnectedSocket();
}
} // anonymous namespace
UnixDomainServerSocket::UnixDomainServerSocket(
const AuthCallback& auth_callback,
bool use_abstract_namespace)
: auth_callback_(auth_callback),
use_abstract_namespace_(use_abstract_namespace) {
DCHECK(!auth_callback_.is_null());
}
UnixDomainServerSocket::~UnixDomainServerSocket() {
}
// static
bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket,
Credentials* credentials) {
#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;
credentials->process_id = user_cred.pid;
credentials->user_id = user_cred.uid;
credentials->group_id = user_cred.gid;
return true;
#else
return getpeereid(
socket, &credentials->user_id, &credentials->group_id) == 0;
#endif
}
int UnixDomainServerSocket::Listen(const IPEndPoint& address, int backlog) {
NOTIMPLEMENTED();
return ERR_NOT_IMPLEMENTED;
}
int UnixDomainServerSocket::ListenWithAddressAndPort(
const std::string& address_string,
uint16_t port,
int backlog) {
NOTIMPLEMENTED();
return ERR_NOT_IMPLEMENTED;
}
int UnixDomainServerSocket::BindAndListen(const std::string& socket_path,
int backlog) {
DCHECK(!listen_socket_);
SockaddrStorage address;
if (!UnixDomainClientSocket::FillAddress(socket_path,
use_abstract_namespace_,
&address)) {
return ERR_ADDRESS_INVALID;
}
std::unique_ptr<SocketPosix> socket(new SocketPosix);
int rv = socket->Open(AF_UNIX);
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv != OK)
return rv;
rv = socket->Bind(address);
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv != OK) {
PLOG(ERROR)
<< "Could not bind unix domain socket to " << socket_path
<< (use_abstract_namespace_ ? " (with abstract namespace)" : "");
return rv;
}
rv = socket->Listen(backlog);
DCHECK_NE(ERR_IO_PENDING, rv);
if (rv != OK)
return rv;
listen_socket_.swap(socket);
return rv;
}
int UnixDomainServerSocket::GetLocalAddress(IPEndPoint* address) const {
DCHECK(address);
// Unix domain sockets have no valid associated addr/port;
// return address invalid.
return ERR_ADDRESS_INVALID;
}
int UnixDomainServerSocket::Accept(std::unique_ptr<StreamSocket>* socket,
const CompletionCallback& callback) {
DCHECK(socket);
SetterCallback setter_callback = base::Bind(&SetStreamSocket, socket);
return DoAccept(setter_callback, callback);
}
int UnixDomainServerSocket::AcceptSocketDescriptor(
SocketDescriptor* socket,
const CompletionCallback& callback) {
DCHECK(socket);
SetterCallback setter_callback = base::Bind(&SetSocketDescriptor, socket);
return DoAccept(setter_callback, callback);
}
int UnixDomainServerSocket::DoAccept(const SetterCallback& setter_callback,
const CompletionCallback& callback) {
DCHECK(!setter_callback.is_null());
DCHECK(!callback.is_null());
DCHECK(listen_socket_);
DCHECK(!accept_socket_);
while (true) {
int rv = listen_socket_->Accept(
&accept_socket_,
base::Bind(&UnixDomainServerSocket::AcceptCompleted,
base::Unretained(this),
setter_callback,
callback));
if (rv != OK)
return rv;
if (AuthenticateAndGetStreamSocket(setter_callback))
return OK;
// Accept another socket because authentication error should be transparent
// to the caller.
}
}
void UnixDomainServerSocket::AcceptCompleted(
const SetterCallback& setter_callback,
const CompletionCallback& callback,
int rv) {
if (rv != OK) {
callback.Run(rv);
return;
}
if (AuthenticateAndGetStreamSocket(setter_callback)) {
callback.Run(OK);
return;
}
// Accept another socket because authentication error should be transparent
// to the caller.
rv = DoAccept(setter_callback, callback);
if (rv != ERR_IO_PENDING)
callback.Run(rv);
}
bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket(
const SetterCallback& setter_callback) {
DCHECK(accept_socket_);
Credentials credentials;
if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) ||
!auth_callback_.Run(credentials)) {
accept_socket_.reset();
return false;
}
setter_callback.Run(std::move(accept_socket_));
return true;
}
} // namespace net