1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 // OS_LINUX only. Note that OS_ANDROID/OS_NACL don't reach here.
31 #if defined(OS_LINUX)
32 
33 #include "ipc/ipc.h"
34 
35 #include <arpa/inet.h>
36 #include <fcntl.h>
37 #include <libgen.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <sys/un.h>
44 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
45 #include <sys/ucred.h>
46 #endif
47 #include <sys/wait.h>
48 #include <unistd.h>
49 
50 #include <cerrno>
51 #include <cstring>
52 #include <cstdlib>
53 
54 #include "base/file_util.h"
55 #include "base/logging.h"
56 #include "base/thread.h"
57 #include "ipc/ipc_path_manager.h"
58 
59 #ifndef UNIX_PATH_MAX
60 #define UNIX_PATH_MAX 108
61 #endif  // UNIX_PATH_MAX
62 
63 namespace mozc {
64 
65 namespace {
66 
67 const int kInvalidSocket = -1;
68 
mkdir_p(const string & dirname)69 void mkdir_p(const string &dirname) {
70   const string parent_dir = FileUtil::Dirname(dirname);
71   struct stat st;
72   if (!parent_dir.empty() &&
73       ::stat(parent_dir.c_str(), &st) < 0) {
74     mkdir_p(parent_dir);
75   }
76   FileUtil::CreateDirectory(dirname);
77 }
78 
IsReadTimeout(int socket,int timeout)79 bool IsReadTimeout(int socket, int timeout) {
80   if (timeout < 0) {
81     return false;
82   }
83   fd_set fds;
84   struct timeval tv;
85   FD_ZERO(&fds);
86   FD_SET(socket, &fds);
87   tv.tv_sec = timeout / 1000;
88   tv.tv_usec = 1000 * (timeout % 1000);
89   if (select(socket + 1, &fds, NULL, NULL, &tv) < 0) {
90     // Mac OS X and glibc implementations of strerror() return a pointer to a
91     // string literal whenever errno is in a valid range, and thus thread-safe.
92     // Probably we don't have to use the cumbersome strerror_r() function.
93     LOG(WARNING) << "select() failed: " << strerror(errno);
94     return true;
95   }
96   if (FD_ISSET(socket, &fds)) {
97     return false;
98   }
99 
100   LOG(ERROR) << "FD_ISSET failed";
101   return true;
102 }
103 
IsWriteTimeout(int socket,int timeout)104 bool IsWriteTimeout(int socket, int timeout) {
105   if (timeout < 0) {
106     return false;
107   }
108   fd_set fds;
109   struct timeval tv;
110   FD_ZERO(&fds);
111   FD_SET(socket, &fds);
112   tv.tv_sec = timeout / 1000;
113   tv.tv_usec = 1000 * (timeout % 1000);
114   if (select(socket + 1, NULL, &fds, NULL, &tv) < 0) {
115     LOG(WARNING) << "select() failed: " << strerror(errno);
116     return true;
117   }
118   if (FD_ISSET(socket, &fds)) {
119     return false;
120   }
121 
122   LOG(ERROR) << "FD_ISSET failed";
123   return true;
124 }
125 
IsPeerValid(int socket,pid_t * pid)126 bool IsPeerValid(int socket, pid_t *pid) {
127   *pid = 0;
128 
129 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
130   // If the OS is MAC, we should validate the peer by using LOCAL_PEERCRED.
131   struct xucred peer_cred;
132   socklen_t peer_cred_len = sizeof(struct xucred);
133   if (::getsockopt(socket, 0, LOCAL_PEERCRED,
134                    &peer_cred, &peer_cred_len) < 0) {
135     LOG(ERROR) << "cannot get peer credential.  NOT a Unix socket?";
136     return false;
137   }
138   if (peer_cred.cr_version != XUCRED_VERSION) {
139     LOG(WARNING) << "credential version mismatch.";
140     return false;
141   }
142   if (peer_cred.cr_uid != ::geteuid()) {
143     LOG(WARNING) << "uid mismatch." << peer_cred.cr_uid << "!=" << ::geteuid();
144     return false;
145   }
146 
147   // MacOS doesn't have cr_pid;
148   *pid = 0;
149 #endif
150 
151 #if defined(OS_LINUX) && !defined(OS_DRAGONFLY)
152   // On ARM Linux, we do nothing and just return true since the platform
153   // sometimes doesn't support the getsockopt(sock, SOL_SOCKET, SO_PEERCRED)
154   // system call.
155   // TODO(yusukes): Add implementation for ARM Linux.
156 #ifndef __arm__
157   struct ucred peer_cred;
158   int peer_cred_len = sizeof(peer_cred);
159   if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED,
160                  reinterpret_cast<void *>(&peer_cred),
161                  reinterpret_cast<socklen_t *>(&peer_cred_len)) < 0) {
162     LOG(ERROR) << "cannot get peer credential. Not a Unix socket?";
163     return false;
164   }
165 
166   if (peer_cred.uid != ::geteuid()) {
167     LOG(WARNING) << "uid mismatch." << peer_cred.uid << "!=" << ::geteuid();
168     return false;
169   }
170 
171   *pid = peer_cred.pid;
172 #endif  // __arm__
173 #endif
174 
175   return true;
176 }
177 
SendMessage(int socket,const char * buf,size_t buf_length,int timeout,IPCErrorType * last_ipc_error)178 bool SendMessage(int socket,
179                  const char *buf,
180                  size_t buf_length, int timeout,
181                  IPCErrorType *last_ipc_error) {
182   size_t buf_length_left = buf_length;
183   while (buf_length_left > 0) {
184     if (IsWriteTimeout(socket, timeout)) {
185       LOG(WARNING) << "Write timeout " << timeout;
186       *last_ipc_error = IPC_TIMEOUT_ERROR;
187       return false;
188     }
189     const ssize_t l = ::send(socket, buf, buf_length_left, MSG_NOSIGNAL);
190     if (l < 0) {
191       // An error occurs.
192       LOG(ERROR) << "an error occurred during sending \""
193                  << string(buf, buf_length_left) << "\": " << strerror(errno);
194       *last_ipc_error = IPC_WRITE_ERROR;
195       return false;
196     }
197     buf += l;
198     buf_length_left -= l;
199   }
200   VLOG(1) << buf_length << " bytes sent";
201   return true;
202 }
203 
RecvMessage(int socket,char * buf,size_t * buf_length,int timeout,IPCErrorType * last_ipc_error)204 bool RecvMessage(int socket,
205                  char *buf,
206                  size_t *buf_length,
207                  int timeout,
208                  IPCErrorType *last_ipc_error) {
209   if (*buf_length == 0) {
210     LOG(WARNING) << "buf_length is 0";
211     *last_ipc_error = IPC_UNKNOWN_ERROR;
212     return false;
213   }
214   ssize_t buf_left = *buf_length;
215   ssize_t read_length = 0;
216   *buf_length = 0;
217   do {
218     if (IsReadTimeout(socket, timeout)) {
219       LOG(WARNING) << "Read timeout " << timeout;
220       *last_ipc_error = IPC_TIMEOUT_ERROR;
221       return false;
222     }
223     read_length = ::recv(socket, buf, buf_left, 0);
224     if (read_length < 0) {
225       LOG(ERROR) << "an error occurred during recv(): " << strerror(errno);
226       *buf_length = 0;
227       *last_ipc_error = IPC_READ_ERROR;
228       return false;
229     }
230     *buf_length += read_length;
231     buf += read_length;
232     buf_left -= read_length;
233   } while (read_length != 0 && buf_left > 0);
234   VLOG(1) << *buf_length << " bytes received";
235   return true;
236 }
237 
SetCloseOnExecFlag(int fd)238 void SetCloseOnExecFlag(int fd) {
239   int flags = ::fcntl(fd, F_GETFD, 0);
240   if (flags < 0) {
241     LOG(WARNING) << "fcntl(F_GETFD) for fd " << fd << " failed: "
242                  << strerror(errno);
243     return;
244   }
245   flags |= FD_CLOEXEC;
246   if (::fcntl(fd, F_SETFD, flags) != 0) {
247     LOG(WARNING) << "fcntl(F_SETFD) for fd " << fd << " failed: "
248                  << strerror(errno);
249   }
250 }
251 
252 // Returns true if address is in abstract namespace. See unix(7) on Linux for
253 // details.
IsAbstractSocket(const string & address)254 bool IsAbstractSocket(const string& address) {
255   return (!address.empty()) && (address[0] == '\0');
256 }
257 }  // namespace
258 
259 // Client
IPCClient(const string & name)260 IPCClient::IPCClient(const string &name)
261     : socket_(kInvalidSocket), connected_(false),
262       ipc_path_manager_(NULL),
263       last_ipc_error_(IPC_NO_ERROR) {
264   Init(name, "");
265 }
266 
IPCClient(const string & name,const string & server_path)267 IPCClient::IPCClient(const string &name, const string &server_path)
268     : socket_(kInvalidSocket), connected_(false),
269       ipc_path_manager_(NULL),
270       last_ipc_error_(IPC_NO_ERROR) {
271   Init(name, server_path);
272 }
273 
Init(const string & name,const string & server_path)274 void IPCClient::Init(const string &name, const string &server_path) {
275   last_ipc_error_ = IPC_NO_CONNECTION;
276 
277   // Try twice, because key may be changed.
278   IPCPathManager *manager = IPCPathManager::GetIPCPathManager(name);
279   if (manager == NULL) {
280     LOG(ERROR) << "IPCPathManager::GetIPCPathManager failed";
281     return;
282   }
283 
284   ipc_path_manager_ = manager;
285 
286   for (size_t trial = 0; trial < 2; ++trial) {
287     string server_address;
288     if (!manager->LoadPathName() || !manager->GetPathName(&server_address)) {
289       continue;
290     }
291     sockaddr_un address;
292     ::memset(&address, 0, sizeof(address));
293     const size_t server_address_length =
294         (server_address.size() >= UNIX_PATH_MAX) ?
295         UNIX_PATH_MAX - 1 : server_address.size();
296     if (server_address.size() >= UNIX_PATH_MAX) {
297       LOG(WARNING) << "too long path: " << server_address;
298     }
299     socket_ = socket(PF_UNIX, SOCK_STREAM, 0);
300     if (socket_ < 0) {
301       LOG(WARNING) << "socket failed: " << strerror(errno);
302       continue;
303     }
304     SetCloseOnExecFlag(socket_);
305     address.sun_family = AF_UNIX;
306     ::memcpy(address.sun_path, server_address.data(), server_address_length);
307     address.sun_path[server_address_length] = '\0';
308 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
309     address.sun_len = SUN_LEN(&address);
310     const size_t sun_len = sizeof(address);
311 #else
312     const size_t sun_len = sizeof(address.sun_family) + server_address_length;
313 #endif
314     pid_t pid = 0;
315     if (::connect(socket_,
316                   reinterpret_cast<const sockaddr*>(&address),
317                   sun_len) != 0 ||
318         !IsPeerValid(socket_, &pid)) {
319       if ((errno == ENOTSOCK || errno == ECONNREFUSED) &&
320           !IsAbstractSocket(server_address)) {
321         // If abstract namepace is not enabled, recreate server_addresss path.
322         ::unlink(server_address.c_str());
323       }
324       LOG(WARNING) << "connect failed: " << strerror(errno);
325       connected_ = false;
326       manager->Clear();
327       continue;
328     } else {
329       if (!manager->IsValidServer(static_cast<uint32>(pid),
330                                   server_path)) {
331         LOG(ERROR) << "Connecting to invalid server";
332         last_ipc_error_ = IPC_INVALID_SERVER;
333         break;
334       }
335       last_ipc_error_ = IPC_NO_ERROR;
336       connected_ = true;
337       break;
338     }
339   }
340 }
341 
~IPCClient()342 IPCClient::~IPCClient() {
343   if (socket_ != kInvalidSocket) {
344     if (::close(socket_) < 0) {
345       LOG(WARNING) << "close failed: " << strerror(errno);
346     }
347     socket_ = kInvalidSocket;
348   }
349   connected_ = false;
350   VLOG(1) << "connection closed (IPCClient destructed)";
351 }
352 
353 // RPC call
Call(const char * request_,size_t input_length,char * response_,size_t * response_size,int32 timeout)354 bool IPCClient::Call(const char *request_,
355                      size_t input_length,
356                      char *response_,
357                      size_t *response_size,
358                      int32 timeout) {
359   last_ipc_error_ = IPC_NO_ERROR;
360   if (!SendMessage(socket_, request_, input_length, timeout,
361                    &last_ipc_error_)) {
362     LOG(ERROR) << "SendMessage failed";
363     return false;
364   }
365 
366   // Half-close the socket so that mozc_server could know the length of the
367   // request data. Without this, RecvMessage() in mozc_server would fail with
368   // timeout.
369   // TODO(yusukes): It would be also possible to modify Send and RecvMessage()
370   // so that they send/recv the payload size explicitly together with the actual
371   // data. Will revisit later.
372   ::shutdown(socket_, SHUT_WR);
373 
374   if (!RecvMessage(socket_, response_, response_size, timeout,
375                    &last_ipc_error_)) {
376     LOG(ERROR) << "RecvMessage failed";
377     return false;
378   }
379   VLOG(1) << "Call succeeded";
380   return true;
381 }
382 
Connected() const383 bool IPCClient::Connected() const {
384   return connected_;
385 }
386 
387 // Server
IPCServer(const string & name,int32 num_connections,int32 timeout)388 IPCServer::IPCServer(const string &name,
389                      int32 num_connections,
390                      int32 timeout)
391     : connected_(false), socket_(kInvalidSocket), timeout_(timeout) {
392   IPCPathManager *manager = IPCPathManager::GetIPCPathManager(name);
393   if (!manager->CreateNewPathName() && !manager->LoadPathName()) {
394     LOG(ERROR) << "Cannot prepare IPC path name";
395     return;
396   }
397 
398   if (!manager->GetPathName(&server_address_)) {
399     LOG(ERROR) << "Cannot make IPC path name";
400     return;
401   }
402   DCHECK(!server_address_.empty());
403 
404   const string dirname = FileUtil::Dirname(server_address_);
405   mkdir_p(dirname);
406 
407   sockaddr_un addr;
408   ::memset(&addr, 0, sizeof(addr));
409   socket_ = ::socket(PF_UNIX, SOCK_STREAM, 0);
410   if (socket_ < 0) {
411     LOG(WARNING) << "socket failed: " << strerror(errno);
412     return;
413   }
414   SetCloseOnExecFlag(socket_);
415 
416   if (server_address_.size() >= UNIX_PATH_MAX) {
417     LOG(WARNING) << "server address is too long";
418     return;
419   }
420 
421   addr.sun_family = AF_UNIX;
422   ::memcpy(addr.sun_path,
423            server_address_.data(),
424            server_address_.size());
425   addr.sun_path[server_address_.size()] = '\0';
426 
427   int on = 1;
428   ::setsockopt(socket_,
429                SOL_SOCKET,
430                SO_REUSEADDR,
431                reinterpret_cast<char *>(&on),
432                sizeof(on));
433 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
434   addr.sun_len = SUN_LEN(&addr);
435   const size_t sun_len = sizeof(addr);
436 #else
437   const size_t sun_len = sizeof(addr.sun_family) + server_address_.size();
438 #endif
439   if (::bind(socket_, reinterpret_cast<sockaddr *>(&addr), sun_len) != 0) {
440     // The UNIX domain socket file (server_address_) already exists?
441     LOG(FATAL) << "bind() failed: " << strerror(errno);
442     return;
443   }
444   if (!IsAbstractSocket(server_address_)) {
445     // Linux does not use files for IPC.
446     ::chmod(server_address_.c_str(), 0600);
447   }
448 
449   if (::listen(socket_, num_connections) < 0) {
450     LOG(FATAL) << "listen() failed: " << strerror(errno);
451     return;
452   }
453 
454   if (!manager->SavePathName()) {
455     LOG(ERROR) << "Cannot save IPC path name";
456     return;
457   }
458 
459   connected_ = true;
460   VLOG(1) << "IPCServer ready";
461 }
462 
~IPCServer()463 IPCServer::~IPCServer() {
464   if (server_thread_.get() != NULL) {
465     server_thread_->Terminate();
466   }
467   ::shutdown(socket_, SHUT_RDWR);
468   ::close(socket_);
469   if (!IsAbstractSocket(server_address_)) {
470     // When abstract namespace is used, unlink() is not necessary.
471     ::unlink(server_address_.c_str());
472   }
473   connected_ = false;
474   socket_ = kInvalidSocket;
475   VLOG(1) << "IPCServer destructed";
476 }
477 
Connected() const478 bool IPCServer::Connected() const {
479   return connected_;
480 }
481 
Loop()482 void IPCServer::Loop() {
483   // The most portable and straightforward single-thread server
484   bool error = false;
485   IPCErrorType last_ipc_error = IPC_NO_ERROR;
486   pid_t pid = 0;
487   while (!error) {
488     const int new_sock = ::accept(socket_, NULL, NULL);
489     if (new_sock < 0) {
490       LOG(FATAL) << "accept() failed: " << strerror(errno);
491       return;
492     }
493     if (!IsPeerValid(new_sock, &pid)) {
494       continue;
495     }
496     size_t request_size = sizeof(request_);
497     size_t response_size = sizeof(response_);
498     if (RecvMessage(new_sock,
499                     &request_[0],
500                     &request_size, timeout_, &last_ipc_error)) {
501       if (!Process(&request_[0], request_size,
502                    &response_[0], &response_size)) {
503         LOG(WARNING) << "Process() failed";
504         error = true;
505       }
506       if (response_size > 0) {
507         SendMessage(new_sock,
508                     &response_[0],
509                     response_size, timeout_, &last_ipc_error);
510       }
511     }
512 
513     ::close(new_sock);
514   }
515 
516   ::shutdown(socket_, SHUT_RDWR);
517   ::close(socket_);
518   if (!IsAbstractSocket(server_address_)) {
519     // When abstract namespace is used, unlink() is not necessary.
520     ::unlink(server_address_.c_str());
521   }
522   connected_ = false;
523   socket_ = kInvalidSocket;
524 }
525 
Terminate()526 void IPCServer::Terminate() {
527   server_thread_->Terminate();
528 }
529 
530 }  // namespace mozc
531 
532 #endif  // OS_LINUX
533