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.
5 #include "net/socket/unix_domain_socket_posix.h"
11 #include <sys/socket.h>
13 #include <sys/types.h>
17 #include "base/bind.h"
18 #include "base/callback.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/threading/platform_thread.h"
21 #include "build/build_config.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/net_util.h"
29 bool NoAuthenticationCallback(uid_t
, gid_t
) {
33 bool GetPeerIds(int socket
, uid_t
* user_id
, gid_t
* group_id
) {
34 #if defined(OS_LINUX) || defined(OS_ANDROID)
35 struct ucred user_cred
;
36 socklen_t len
= sizeof(user_cred
);
37 if (getsockopt(socket
, SOL_SOCKET
, SO_PEERCRED
, &user_cred
, &len
) == -1)
39 *user_id
= user_cred
.uid
;
40 *group_id
= user_cred
.gid
;
42 if (getpeereid(socket
, user_id
, group_id
) == -1)
51 UnixDomainSocket::AuthCallback
NoAuthentication() {
52 return base::Bind(NoAuthenticationCallback
);
56 UnixDomainSocket
* UnixDomainSocket::CreateAndListenInternal(
57 const std::string
& path
,
58 StreamListenSocket::Delegate
* del
,
59 const AuthCallback
& auth_callback
,
60 bool use_abstract_namespace
) {
61 SocketDescriptor s
= CreateAndBind(path
, use_abstract_namespace
);
62 if (s
== kInvalidSocket
)
64 UnixDomainSocket
* sock
= new UnixDomainSocket(s
, del
, auth_callback
);
70 scoped_refptr
<UnixDomainSocket
> UnixDomainSocket::CreateAndListen(
71 const std::string
& path
,
72 StreamListenSocket::Delegate
* del
,
73 const AuthCallback
& auth_callback
) {
74 return CreateAndListenInternal(path
, del
, auth_callback
, false);
77 #if defined(SOCKET_ABSTRACT_NAMESPACE_SUPPORTED)
79 scoped_refptr
<UnixDomainSocket
>
80 UnixDomainSocket::CreateAndListenWithAbstractNamespace(
81 const std::string
& path
,
82 StreamListenSocket::Delegate
* del
,
83 const AuthCallback
& auth_callback
) {
84 return make_scoped_refptr(
85 CreateAndListenInternal(path
, del
, auth_callback
, true));
89 UnixDomainSocket::UnixDomainSocket(
91 StreamListenSocket::Delegate
* del
,
92 const AuthCallback
& auth_callback
)
93 : StreamListenSocket(s
, del
),
94 auth_callback_(auth_callback
) {}
96 UnixDomainSocket::~UnixDomainSocket() {}
99 SocketDescriptor
UnixDomainSocket::CreateAndBind(const std::string
& path
,
100 bool use_abstract_namespace
) {
102 static const size_t kPathMax
= sizeof(addr
.sun_path
);
103 if (use_abstract_namespace
+ path
.size() + 1 /* '\0' */ > kPathMax
)
104 return kInvalidSocket
;
105 const SocketDescriptor s
= socket(PF_UNIX
, SOCK_STREAM
, 0);
106 if (s
== kInvalidSocket
)
107 return kInvalidSocket
;
108 memset(&addr
, 0, sizeof(addr
));
109 addr
.sun_family
= AF_UNIX
;
111 if (use_abstract_namespace
) {
112 // Convert the path given into abstract socket name. It must start with
113 // the '\0' character, so we are adding it. |addr_len| must specify the
114 // length of the structure exactly, as potentially the socket name may
115 // have '\0' characters embedded (although we don't support this).
116 // Note that addr.sun_path is already zero initialized.
117 memcpy(addr
.sun_path
+ 1, path
.c_str(), path
.size());
118 addr_len
= path
.size() + offsetof(struct sockaddr_un
, sun_path
) + 1;
120 memcpy(addr
.sun_path
, path
.c_str(), path
.size());
121 addr_len
= sizeof(sockaddr_un
);
123 if (bind(s
, reinterpret_cast<sockaddr
*>(&addr
), addr_len
)) {
124 LOG(ERROR
) << "Could not bind unix domain socket to " << path
;
125 if (use_abstract_namespace
)
126 LOG(ERROR
) << " (with abstract namespace enabled)";
127 if (HANDLE_EINTR(close(s
)) < 0)
128 LOG(ERROR
) << "close() error";
129 return kInvalidSocket
;
134 void UnixDomainSocket::Accept() {
135 SocketDescriptor conn
= StreamListenSocket::AcceptSocket();
136 if (conn
== kInvalidSocket
)
140 if (!GetPeerIds(conn
, &user_id
, &group_id
) ||
141 !auth_callback_
.Run(user_id
, group_id
)) {
142 if (HANDLE_EINTR(close(conn
)) < 0)
143 LOG(ERROR
) << "close() error";
146 scoped_refptr
<UnixDomainSocket
> sock(
147 new UnixDomainSocket(conn
, socket_delegate_
, auth_callback_
));
148 // It's up to the delegate to AddRef if it wants to keep it around.
149 sock
->WatchSocket(WAITING_READ
);
150 socket_delegate_
->DidAccept(this, sock
);
153 UnixDomainSocketFactory::UnixDomainSocketFactory(
154 const std::string
& path
,
155 const UnixDomainSocket::AuthCallback
& auth_callback
)
157 auth_callback_(auth_callback
) {}
159 UnixDomainSocketFactory::~UnixDomainSocketFactory() {}
161 scoped_refptr
<StreamListenSocket
> UnixDomainSocketFactory::CreateAndListen(
162 StreamListenSocket::Delegate
* delegate
) const {
163 return UnixDomainSocket::CreateAndListen(path_
, delegate
, auth_callback_
);
166 #if defined(SOCKET_ABSTRACT_NAMESPACE_SUPPORTED)
168 UnixDomainSocketWithAbstractNamespaceFactory::
169 UnixDomainSocketWithAbstractNamespaceFactory(
170 const std::string
& path
,
171 const UnixDomainSocket::AuthCallback
& auth_callback
)
172 : UnixDomainSocketFactory(path
, auth_callback
) {}
174 UnixDomainSocketWithAbstractNamespaceFactory::
175 ~UnixDomainSocketWithAbstractNamespaceFactory() {}
177 scoped_refptr
<StreamListenSocket
>
178 UnixDomainSocketWithAbstractNamespaceFactory::CreateAndListen(
179 StreamListenSocket::Delegate
* delegate
) const {
180 return UnixDomainSocket::CreateAndListenWithAbstractNamespace(
181 path_
, delegate
, auth_callback_
);