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"
24 #include "net/socket/socket_descriptor.h"
30 bool NoAuthenticationCallback(uid_t
, gid_t
) {
34 bool GetPeerIds(int socket
, uid_t
* user_id
, gid_t
* group_id
) {
35 #if defined(OS_LINUX) || defined(OS_ANDROID)
36 struct ucred user_cred
;
37 socklen_t len
= sizeof(user_cred
);
38 if (getsockopt(socket
, SOL_SOCKET
, SO_PEERCRED
, &user_cred
, &len
) == -1)
40 *user_id
= user_cred
.uid
;
41 *group_id
= user_cred
.gid
;
43 if (getpeereid(socket
, user_id
, group_id
) == -1)
52 UnixDomainSocket::AuthCallback
UnixDomainSocket::NoAuthentication() {
53 return base::Bind(NoAuthenticationCallback
);
57 UnixDomainSocket
* UnixDomainSocket::CreateAndListenInternal(
58 const std::string
& path
,
59 const std::string
& fallback_path
,
60 StreamListenSocket::Delegate
* del
,
61 const AuthCallback
& auth_callback
,
62 bool use_abstract_namespace
) {
63 SocketDescriptor s
= CreateAndBind(path
, use_abstract_namespace
);
64 if (s
== kInvalidSocket
&& !fallback_path
.empty())
65 s
= CreateAndBind(fallback_path
, use_abstract_namespace
);
66 if (s
== kInvalidSocket
)
68 UnixDomainSocket
* sock
= new UnixDomainSocket(s
, del
, auth_callback
);
74 scoped_refptr
<UnixDomainSocket
> UnixDomainSocket::CreateAndListen(
75 const std::string
& path
,
76 StreamListenSocket::Delegate
* del
,
77 const AuthCallback
& auth_callback
) {
78 return CreateAndListenInternal(path
, "", del
, auth_callback
, false);
81 #if defined(SOCKET_ABSTRACT_NAMESPACE_SUPPORTED)
83 scoped_refptr
<UnixDomainSocket
>
84 UnixDomainSocket::CreateAndListenWithAbstractNamespace(
85 const std::string
& path
,
86 const std::string
& fallback_path
,
87 StreamListenSocket::Delegate
* del
,
88 const AuthCallback
& auth_callback
) {
89 return make_scoped_refptr(
90 CreateAndListenInternal(path
, fallback_path
, del
, auth_callback
, true));
94 UnixDomainSocket::UnixDomainSocket(
96 StreamListenSocket::Delegate
* del
,
97 const AuthCallback
& auth_callback
)
98 : StreamListenSocket(s
, del
),
99 auth_callback_(auth_callback
) {}
101 UnixDomainSocket::~UnixDomainSocket() {}
104 SocketDescriptor
UnixDomainSocket::CreateAndBind(const std::string
& path
,
105 bool use_abstract_namespace
) {
107 static const size_t kPathMax
= sizeof(addr
.sun_path
);
108 if (use_abstract_namespace
+ path
.size() + 1 /* '\0' */ > kPathMax
)
109 return kInvalidSocket
;
110 const SocketDescriptor s
= CreatePlatformSocket(PF_UNIX
, SOCK_STREAM
, 0);
111 if (s
== kInvalidSocket
)
112 return kInvalidSocket
;
113 memset(&addr
, 0, sizeof(addr
));
114 addr
.sun_family
= AF_UNIX
;
116 if (use_abstract_namespace
) {
117 // Convert the path given into abstract socket name. It must start with
118 // the '\0' character, so we are adding it. |addr_len| must specify the
119 // length of the structure exactly, as potentially the socket name may
120 // have '\0' characters embedded (although we don't support this).
121 // Note that addr.sun_path is already zero initialized.
122 memcpy(addr
.sun_path
+ 1, path
.c_str(), path
.size());
123 addr_len
= path
.size() + offsetof(struct sockaddr_un
, sun_path
) + 1;
125 memcpy(addr
.sun_path
, path
.c_str(), path
.size());
126 addr_len
= sizeof(sockaddr_un
);
128 if (bind(s
, reinterpret_cast<sockaddr
*>(&addr
), addr_len
)) {
129 LOG(ERROR
) << "Could not bind unix domain socket to " << path
;
130 if (use_abstract_namespace
)
131 LOG(ERROR
) << " (with abstract namespace enabled)";
132 if (HANDLE_EINTR(close(s
)) < 0)
133 LOG(ERROR
) << "close() error";
134 return kInvalidSocket
;
139 void UnixDomainSocket::Accept() {
140 SocketDescriptor conn
= StreamListenSocket::AcceptSocket();
141 if (conn
== kInvalidSocket
)
145 if (!GetPeerIds(conn
, &user_id
, &group_id
) ||
146 !auth_callback_
.Run(user_id
, group_id
)) {
147 if (HANDLE_EINTR(close(conn
)) < 0)
148 LOG(ERROR
) << "close() error";
151 scoped_refptr
<UnixDomainSocket
> sock(
152 new UnixDomainSocket(conn
, socket_delegate_
, auth_callback_
));
153 // It's up to the delegate to AddRef if it wants to keep it around.
154 sock
->WatchSocket(WAITING_READ
);
155 socket_delegate_
->DidAccept(this, sock
.get());
158 UnixDomainSocketFactory::UnixDomainSocketFactory(
159 const std::string
& path
,
160 const UnixDomainSocket::AuthCallback
& auth_callback
)
162 auth_callback_(auth_callback
) {}
164 UnixDomainSocketFactory::~UnixDomainSocketFactory() {}
166 scoped_refptr
<StreamListenSocket
> UnixDomainSocketFactory::CreateAndListen(
167 StreamListenSocket::Delegate
* delegate
) const {
168 return UnixDomainSocket::CreateAndListen(
169 path_
, delegate
, auth_callback_
);
172 #if defined(SOCKET_ABSTRACT_NAMESPACE_SUPPORTED)
174 UnixDomainSocketWithAbstractNamespaceFactory::
175 UnixDomainSocketWithAbstractNamespaceFactory(
176 const std::string
& path
,
177 const std::string
& fallback_path
,
178 const UnixDomainSocket::AuthCallback
& auth_callback
)
179 : UnixDomainSocketFactory(path
, auth_callback
),
180 fallback_path_(fallback_path
) {}
182 UnixDomainSocketWithAbstractNamespaceFactory::
183 ~UnixDomainSocketWithAbstractNamespaceFactory() {}
185 scoped_refptr
<StreamListenSocket
>
186 UnixDomainSocketWithAbstractNamespaceFactory::CreateAndListen(
187 StreamListenSocket::Delegate
* delegate
) const {
188 return UnixDomainSocket::CreateAndListenWithAbstractNamespace(
189 path_
, fallback_path_
, delegate
, auth_callback_
);