Home | History | Annotate | Download | only in socket
      1 // 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 
      5 #include "net/socket/unix_domain_server_socket_posix.h"
      6 
      7 #include <errno.h>
      8 #include <sys/socket.h>
      9 #include <sys/un.h>
     10 #include <unistd.h>
     11 
     12 #include "base/logging.h"
     13 #include "net/base/net_errors.h"
     14 #include "net/socket/socket_libevent.h"
     15 #include "net/socket/unix_domain_client_socket_posix.h"
     16 
     17 namespace net {
     18 
     19 namespace {
     20 
     21 // Intended for use as SetterCallbacks in Accept() helper methods.
     22 void SetStreamSocket(scoped_ptr<StreamSocket>* socket,
     23                      scoped_ptr<SocketLibevent> accepted_socket) {
     24   socket->reset(new UnixDomainClientSocket(accepted_socket.Pass()));
     25 }
     26 
     27 void SetSocketDescriptor(SocketDescriptor* socket,
     28                          scoped_ptr<SocketLibevent> accepted_socket) {
     29   *socket = accepted_socket->ReleaseConnectedSocket();
     30 }
     31 
     32 }  // anonymous namespace
     33 
     34 UnixDomainServerSocket::UnixDomainServerSocket(
     35     const AuthCallback& auth_callback,
     36     bool use_abstract_namespace)
     37     : auth_callback_(auth_callback),
     38       use_abstract_namespace_(use_abstract_namespace) {
     39   DCHECK(!auth_callback_.is_null());
     40 }
     41 
     42 UnixDomainServerSocket::~UnixDomainServerSocket() {
     43 }
     44 
     45 // static
     46 bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket,
     47                                                 Credentials* credentials) {
     48 #if defined(OS_LINUX) || defined(OS_ANDROID)
     49   struct ucred user_cred;
     50   socklen_t len = sizeof(user_cred);
     51   if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) < 0)
     52     return false;
     53   credentials->process_id = user_cred.pid;
     54   credentials->user_id = user_cred.uid;
     55   credentials->group_id = user_cred.gid;
     56   return true;
     57 #else
     58   return getpeereid(
     59       socket, &credentials->user_id, &credentials->group_id) == 0;
     60 #endif
     61 }
     62 
     63 int UnixDomainServerSocket::Listen(const IPEndPoint& address, int backlog) {
     64   NOTIMPLEMENTED();
     65   return ERR_NOT_IMPLEMENTED;
     66 }
     67 
     68 int UnixDomainServerSocket::ListenWithAddressAndPort(
     69     const std::string& unix_domain_path,
     70     int port_unused,
     71     int backlog) {
     72   DCHECK(!listen_socket_);
     73 
     74   SockaddrStorage address;
     75   if (!UnixDomainClientSocket::FillAddress(unix_domain_path,
     76                                            use_abstract_namespace_,
     77                                            &address)) {
     78     return ERR_ADDRESS_INVALID;
     79   }
     80 
     81   scoped_ptr<SocketLibevent> socket(new SocketLibevent);
     82   int rv = socket->Open(AF_UNIX);
     83   DCHECK_NE(ERR_IO_PENDING, rv);
     84   if (rv != OK)
     85     return rv;
     86 
     87   rv = socket->Bind(address);
     88   DCHECK_NE(ERR_IO_PENDING, rv);
     89   if (rv != OK) {
     90     PLOG(ERROR)
     91         << "Could not bind unix domain socket to " << unix_domain_path
     92         << (use_abstract_namespace_ ? " (with abstract namespace)" : "");
     93     return rv;
     94   }
     95 
     96   rv = socket->Listen(backlog);
     97   DCHECK_NE(ERR_IO_PENDING, rv);
     98   if (rv != OK)
     99     return rv;
    100 
    101   listen_socket_.swap(socket);
    102   return rv;
    103 }
    104 
    105 int UnixDomainServerSocket::GetLocalAddress(IPEndPoint* address) const {
    106   NOTIMPLEMENTED();
    107   return ERR_NOT_IMPLEMENTED;
    108 }
    109 
    110 int UnixDomainServerSocket::Accept(scoped_ptr<StreamSocket>* socket,
    111                                    const CompletionCallback& callback) {
    112   DCHECK(socket);
    113 
    114   SetterCallback setter_callback = base::Bind(&SetStreamSocket, socket);
    115   return DoAccept(setter_callback, callback);
    116 }
    117 
    118 int UnixDomainServerSocket::AcceptSocketDescriptor(
    119     SocketDescriptor* socket,
    120     const CompletionCallback& callback) {
    121   DCHECK(socket);
    122 
    123   SetterCallback setter_callback = base::Bind(&SetSocketDescriptor, socket);
    124   return DoAccept(setter_callback, callback);
    125 }
    126 
    127 int UnixDomainServerSocket::DoAccept(const SetterCallback& setter_callback,
    128                                      const CompletionCallback& callback) {
    129   DCHECK(!setter_callback.is_null());
    130   DCHECK(!callback.is_null());
    131   DCHECK(listen_socket_);
    132   DCHECK(!accept_socket_);
    133 
    134   while (true) {
    135     int rv = listen_socket_->Accept(
    136         &accept_socket_,
    137         base::Bind(&UnixDomainServerSocket::AcceptCompleted,
    138                    base::Unretained(this),
    139                    setter_callback,
    140                    callback));
    141     if (rv != OK)
    142       return rv;
    143     if (AuthenticateAndGetStreamSocket(setter_callback))
    144       return OK;
    145     // Accept another socket because authentication error should be transparent
    146     // to the caller.
    147   }
    148 }
    149 
    150 void UnixDomainServerSocket::AcceptCompleted(
    151     const SetterCallback& setter_callback,
    152     const CompletionCallback& callback,
    153     int rv) {
    154   if (rv != OK) {
    155     callback.Run(rv);
    156     return;
    157   }
    158 
    159   if (AuthenticateAndGetStreamSocket(setter_callback)) {
    160     callback.Run(OK);
    161     return;
    162   }
    163 
    164   // Accept another socket because authentication error should be transparent
    165   // to the caller.
    166   rv = DoAccept(setter_callback, callback);
    167   if (rv != ERR_IO_PENDING)
    168     callback.Run(rv);
    169 }
    170 
    171 bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket(
    172     const SetterCallback& setter_callback) {
    173   DCHECK(accept_socket_);
    174 
    175   Credentials credentials;
    176   if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) ||
    177       !auth_callback_.Run(credentials)) {
    178     accept_socket_.reset();
    179     return false;
    180   }
    181 
    182   setter_callback.Run(accept_socket_.Pass());
    183   return true;
    184 }
    185 
    186 }  // namespace net
    187