1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include "system.hxx"
21
22 #include <o3tl/safeint.hxx>
23 #include <osl/pipe.h>
24 #include <osl/diagnose.h>
25 #include <osl/thread.h>
26 #include <osl/interlck.h>
27 #include <rtl/string.h>
28 #include <rtl/ustring.h>
29 #include <rtl/bootstrap.hxx>
30 #include <sal/log.hxx>
31
32 #include "sockimpl.hxx"
33 #include "secimpl.hxx"
34 #include "unixerrnostring.hxx"
35
36 #include <cassert>
37 #include <cstring>
38
39 #define PIPEDEFAULTPATH "/tmp"
40 #define PIPEALTERNATEPATH "/var/tmp"
41
42 static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options, oslSecurity Security);
43
44 struct
45 {
46 int errcode;
47 oslPipeError error;
48 } const PipeError[]= {
49 { 0, osl_Pipe_E_None }, /* no error */
50 { EPROTOTYPE, osl_Pipe_E_NoProtocol }, /* Protocol wrong type for socket */
51 { ENOPROTOOPT, osl_Pipe_E_NoProtocol }, /* Protocol not available */
52 { EPROTONOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol not supported */
53 #ifdef ESOCKTNOSUPPORT
54 { ESOCKTNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Socket type not supported */
55 #endif
56 { EPFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol family not supported */
57 { EAFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Address family not supported by */
58 /* protocol family */
59 { ENETRESET, osl_Pipe_E_NetworkReset }, /* Network dropped connection because */
60 /* of reset */
61 { ECONNABORTED, osl_Pipe_E_ConnectionAbort }, /* Software caused connection abort */
62 { ECONNRESET, osl_Pipe_E_ConnectionReset }, /* Connection reset by peer */
63 { ENOBUFS, osl_Pipe_E_NoBufferSpace }, /* No buffer space available */
64 { ETIMEDOUT, osl_Pipe_E_TimedOut }, /* Connection timed out */
65 { ECONNREFUSED, osl_Pipe_E_ConnectionRefused }, /* Connection refused */
66 { -1, osl_Pipe_E_invalidError }
67 };
68
osl_PipeErrorFromNative(int nativeType)69 static oslPipeError osl_PipeErrorFromNative(int nativeType)
70 {
71 int i = 0;
72
73 while ((PipeError[i].error != osl_Pipe_E_invalidError) &&
74 (PipeError[i].errcode != nativeType))
75 {
76 i++;
77 }
78
79 return PipeError[i].error;
80 }
81
createPipeImpl()82 static oslPipe createPipeImpl()
83 {
84 oslPipe pPipeImpl;
85
86 pPipeImpl = static_cast< oslPipe >(calloc(1, sizeof(struct oslPipeImpl)));
87 if (!pPipeImpl)
88 return nullptr;
89
90 pPipeImpl->m_nRefCount = 1;
91 pPipeImpl->m_bClosed = false;
92 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
93 pPipeImpl->m_bIsInShutdown = false;
94 pPipeImpl->m_bIsAccepting = false;
95 #endif
96
97 return pPipeImpl;
98 }
99
destroyPipeImpl(oslPipe pImpl)100 static void destroyPipeImpl(oslPipe pImpl)
101 {
102 if (pImpl)
103 free(pImpl);
104 }
105
osl_createPipe(rtl_uString * ustrPipeName,oslPipeOptions Options,oslSecurity Security)106 oslPipe SAL_CALL osl_createPipe(rtl_uString *ustrPipeName, oslPipeOptions Options, oslSecurity Security)
107 {
108 oslPipe pPipe = nullptr;
109 rtl_String* strPipeName = nullptr;
110
111 if (ustrPipeName)
112 {
113 rtl_uString2String(&strPipeName,
114 rtl_uString_getStr(ustrPipeName),
115 rtl_uString_getLength(ustrPipeName),
116 osl_getThreadTextEncoding(),
117 OUSTRING_TO_OSTRING_CVTFLAGS);
118 char* pszPipeName = rtl_string_getStr(strPipeName);
119 pPipe = osl_psz_createPipe(pszPipeName, Options, Security);
120
121 if (strPipeName)
122 rtl_string_release(strPipeName);
123 }
124
125 return pPipe;
126
127 }
128
129 static OString
getBootstrapSocketPath()130 getBootstrapSocketPath()
131 {
132 OUString pValue;
133
134 if (rtl::Bootstrap::get("OSL_SOCKET_PATH", pValue))
135 {
136 return OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
137 }
138 return "";
139 }
140
osl_psz_createPipe(const char * pszPipeName,oslPipeOptions Options,oslSecurity Security)141 static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options,
142 oslSecurity Security)
143 {
144 int Flags;
145 size_t len;
146 struct sockaddr_un addr;
147
148 OString name;
149 oslPipe pPipe;
150
151 if (access(PIPEDEFAULTPATH, W_OK) == 0)
152 name = PIPEDEFAULTPATH;
153 else if (access(PIPEALTERNATEPATH, W_OK) == 0)
154 name = PIPEALTERNATEPATH;
155 else {
156 name = getBootstrapSocketPath ();
157 }
158
159 name += "/";
160
161 if (Security)
162 {
163 char Ident[256];
164
165 Ident[0] = '\0';
166
167 OSL_VERIFY(osl_psz_getUserIdent(Security, Ident, sizeof(Ident)));
168
169 name += OString::Concat("OSL_PIPE_") + Ident + "_" + pszPipeName;
170 }
171 else
172 {
173 name += OString::Concat("OSL_PIPE_") + pszPipeName;
174 }
175
176 if (o3tl::make_unsigned(name.getLength()) >= sizeof addr.sun_path)
177 {
178 SAL_WARN("sal.osl.pipe", "osl_createPipe: pipe name too long");
179 return nullptr;
180 }
181
182 /* alloc memory */
183 pPipe = createPipeImpl();
184
185 if (!pPipe)
186 return nullptr;
187
188 /* create socket */
189 pPipe->m_Socket = socket(AF_UNIX, SOCK_STREAM, 0);
190 if (pPipe->m_Socket < 0)
191 {
192 SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
193 destroyPipeImpl(pPipe);
194 return nullptr;
195 }
196
197 /* set close-on-exec flag */
198 if ((Flags = fcntl(pPipe->m_Socket, F_GETFD, 0)) != -1)
199 {
200 Flags |= FD_CLOEXEC;
201 if (fcntl(pPipe->m_Socket, F_SETFD, Flags) == -1)
202 {
203 SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
204 }
205 }
206
207 memset(&addr, 0, sizeof(addr));
208
209 SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe->m_Socket << " '" << name << "'");
210
211 addr.sun_family = AF_UNIX;
212 // coverity[fixed_size_dest : FALSE] - safe, see check above
213 strcpy(addr.sun_path, name.getStr());
214 #if defined(DRAGONFLY)
215 len = SUN_LEN(&addr);
216 #else
217 len = sizeof(addr);
218 #endif
219
220 if (Options & osl_Pipe_CREATE)
221 {
222 struct stat status;
223
224 /* check if there exists an orphan filesystem entry */
225 if ((stat(name.getStr(), &status) == 0) &&
226 (S_ISSOCK(status.st_mode) || S_ISFIFO(status.st_mode)))
227 {
228 if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
229 {
230 close (pPipe->m_Socket);
231 destroyPipeImpl(pPipe);
232 return nullptr;
233 }
234
235 unlink(name.getStr());
236 }
237
238 /* ok, fs clean */
239 if (bind(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) < 0)
240 {
241 SAL_WARN("sal.osl.pipe", "bind() failed: " << UnixErrnoString(errno));
242 close(pPipe->m_Socket);
243 destroyPipeImpl(pPipe);
244 return nullptr;
245 }
246
247 /* Only give access to all if no security handle was specified, otherwise security
248 depends on umask */
249
250 if (!Security)
251 (void)chmod(name.getStr(),S_IRWXU | S_IRWXG |S_IRWXO);
252
253 strcpy(pPipe->m_Name, name.getStr()); // safe, see check above
254
255 if (listen(pPipe->m_Socket, 5) < 0)
256 {
257 SAL_WARN("sal.osl.pipe", "listen() failed: " << UnixErrnoString(errno));
258 // cid#1255391 warns about unlink(name) after stat(name, &status)
259 // above, but the intervening call to bind makes those two clearly
260 // unrelated, as it would fail if name existed at that point in
261 // time:
262 // coverity[toctou] - this is bogus
263 unlink(name.getStr()); /* remove filesystem entry */
264 close(pPipe->m_Socket);
265 destroyPipeImpl(pPipe);
266 return nullptr;
267 }
268
269 return pPipe;
270 }
271
272 /* osl_pipe_OPEN */
273 if (access(name.getStr(), F_OK) != -1)
274 {
275 if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
276 return pPipe;
277
278 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
279 }
280
281 close (pPipe->m_Socket);
282 destroyPipeImpl(pPipe);
283 return nullptr;
284 }
285
osl_acquirePipe(oslPipe pPipe)286 void SAL_CALL osl_acquirePipe(oslPipe pPipe)
287 {
288 osl_atomic_increment(&(pPipe->m_nRefCount));
289 }
290
osl_releasePipe(oslPipe pPipe)291 void SAL_CALL osl_releasePipe(oslPipe pPipe)
292 {
293 if (!pPipe)
294 return;
295
296 if (osl_atomic_decrement(&(pPipe->m_nRefCount)) == 0)
297 {
298 if (!pPipe->m_bClosed)
299 osl_closePipe(pPipe);
300
301 destroyPipeImpl(pPipe);
302 }
303 }
304
osl_closePipe(oslPipe pPipe)305 void SAL_CALL osl_closePipe(oslPipe pPipe)
306 {
307 int nRet;
308 int ConnFD;
309
310 if (!pPipe)
311 return;
312
313 if (pPipe->m_bClosed)
314 return;
315
316 ConnFD = pPipe->m_Socket;
317
318 /* Thread does not return from accept on linux, so
319 connect to the accepting pipe
320 */
321 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
322 struct sockaddr_un addr;
323
324 if (pPipe->m_bIsAccepting)
325 {
326 pPipe->m_bIsInShutdown = true;
327 pPipe->m_Socket = -1;
328
329 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
330 if (fd < 0)
331 {
332 SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
333 return;
334 }
335
336 memset(&addr, 0, sizeof(addr));
337
338 SAL_INFO("sal.osl.pipe", "osl_destroyPipe : Pipe Name '" << pPipe->m_Name << "'");
339
340 addr.sun_family = AF_UNIX;
341 strcpy(addr.sun_path, pPipe->m_Name); // safe, as both are same size
342
343 nRet = connect(fd, reinterpret_cast< sockaddr* >(&addr), sizeof(addr));
344 if (nRet < 0)
345 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
346
347 close(fd);
348 }
349 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
350
351 nRet = shutdown(ConnFD, 2);
352 if (nRet < 0)
353 SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno));
354
355 nRet = close(ConnFD);
356 if (nRet < 0)
357 SAL_WARN("sal.osl.pipe", "close() failed: " << UnixErrnoString(errno));
358
359 /* remove filesystem entry */
360 if (pPipe->m_Name[0] != '\0')
361 unlink(pPipe->m_Name);
362
363 pPipe->m_bClosed = true;
364 }
365
osl_acceptPipe(oslPipe pPipe)366 oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe)
367 {
368 int s;
369 oslPipe pAcceptedPipe;
370
371 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "invalid pipe");
372 if (!pPipe)
373 return nullptr;
374
375 assert(pPipe->m_Name[0] != '\0'); // you cannot have an empty pipe name
376
377 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
378 pPipe->m_bIsAccepting = true;
379 #endif
380
381 s = accept(pPipe->m_Socket, nullptr, nullptr);
382
383 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
384 pPipe->m_bIsAccepting = false;
385 #endif
386
387 if (s < 0)
388 {
389 SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno));
390 return nullptr;
391 }
392
393 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
394 if (pPipe->m_bIsInShutdown)
395 {
396 close(s);
397 return nullptr;
398 }
399 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
400
401 /* alloc memory */
402 pAcceptedPipe = createPipeImpl();
403
404 assert(pAcceptedPipe); // should never be the case that an oslPipe cannot be initialized
405 if (!pAcceptedPipe)
406 {
407 close(s);
408 return nullptr;
409 }
410
411 /* set close-on-exec flag */
412 int flags;
413 if ((flags = fcntl(s, F_GETFD, 0)) >= 0)
414 {
415 flags |= FD_CLOEXEC;
416 if (fcntl(s, F_SETFD, flags) < 0)
417 SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
418 }
419
420 pAcceptedPipe->m_Socket = s;
421
422 return pAcceptedPipe;
423 }
424
osl_receivePipe(oslPipe pPipe,void * pBuffer,sal_Int32 BytesToRead)425 sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe,
426 void* pBuffer,
427 sal_Int32 BytesToRead)
428 {
429 int nRet = 0;
430
431 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
432 if (!pPipe)
433 {
434 SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
435 errno=EINVAL;
436 return -1;
437 }
438
439 nRet = recv(pPipe->m_Socket, pBuffer, BytesToRead, 0);
440
441 if (nRet < 0)
442 SAL_WARN("sal.osl.pipe", "recv() failed: " << UnixErrnoString(errno));
443
444 return nRet;
445 }
446
osl_sendPipe(oslPipe pPipe,const void * pBuffer,sal_Int32 BytesToSend)447 sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
448 const void* pBuffer,
449 sal_Int32 BytesToSend)
450 {
451 int nRet=0;
452
453 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
454 if (!pPipe)
455 {
456 SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
457 errno=EINVAL;
458 return -1;
459 }
460
461 nRet = send(pPipe->m_Socket, pBuffer, BytesToSend, 0);
462
463 if (nRet <= 0)
464 SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno));
465
466 return nRet;
467 }
468
osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe)469 oslPipeError SAL_CALL osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe)
470 {
471 return osl_PipeErrorFromNative(errno);
472 }
473
osl_writePipe(oslPipe pPipe,const void * pBuffer,sal_Int32 n)474 sal_Int32 SAL_CALL osl_writePipe(oslPipe pPipe, const void *pBuffer, sal_Int32 n)
475 {
476 /* loop until all desired bytes were send or an error occurred */
477 sal_Int32 BytesSend = 0;
478 sal_Int32 BytesToSend = n;
479
480 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_writePipe: invalid pipe"); // osl_sendPipe detects invalid pipe
481 while (BytesToSend > 0)
482 {
483 sal_Int32 RetVal;
484
485 RetVal= osl_sendPipe(pPipe, pBuffer, BytesToSend);
486
487 /* error occurred? */
488 if (RetVal <= 0)
489 break;
490
491 BytesToSend -= RetVal;
492 BytesSend += RetVal;
493 pBuffer= static_cast< char const* >(pBuffer) + RetVal;
494 }
495
496 return BytesSend;
497 }
498
osl_readPipe(oslPipe pPipe,void * pBuffer,sal_Int32 n)499 sal_Int32 SAL_CALL osl_readPipe( oslPipe pPipe, void *pBuffer , sal_Int32 n )
500 {
501 /* loop until all desired bytes were read or an error occurred */
502 sal_Int32 BytesRead = 0;
503 sal_Int32 BytesToRead = n;
504
505 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_readPipe: invalid pipe"); // osl_receivePipe detects invalid pipe
506 while (BytesToRead > 0)
507 {
508 sal_Int32 RetVal;
509 RetVal= osl_receivePipe(pPipe, pBuffer, BytesToRead);
510
511 /* error occurred? */
512 if (RetVal <= 0)
513 break;
514
515 BytesToRead -= RetVal;
516 BytesRead += RetVal;
517 pBuffer= static_cast< char* >(pBuffer) + RetVal;
518 }
519
520 return BytesRead;
521 }
522
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
524