2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // NaCl inter-module communication primitives.
35 #define NOMINMAX // Disables the generation of the min and max macro in
38 #include "native_client/intermodule_comm/nacl_imc.h"
43 #include <sys/types.h>
45 #include "native_client/include/atomic_ops.h"
51 const char kPipePrefix
[] = "\\\\.\\pipe\\google-nacl-";
52 const size_t kPipePrefixSize
= sizeof kPipePrefix
/ sizeof kPipePrefix
[0];
54 const int kPipePathMax
= kPipePrefixSize
+ kPathMax
+ 1;
55 const int kOutBufferSize
= 4096; // TBD
56 const int kInBufferSize
= 4096; // TBD
57 const int kDefaultTimeoutMilliSeconds
= 1000;
59 // ControlHeader::command
60 const int kEchoRequest
= 0;
61 const int kEchoResponse
= 1;
62 const int kMessage
= 2;
63 const int kCancel
= 3; // Cancels Handle transfer operations
65 struct ControlHeader
{
68 size_t message_length
;
72 bool GetSocketName(const SocketAddress
* address
, char* name
) {
73 if (address
== NULL
|| !isalpha(address
->path
[0])) {
74 SetLastError(ERROR_INVALID_PARAMETER
);
77 sprintf_s(name
, kPipePathMax
, "%s%.*s",
78 kPipePrefix
, kPathMax
, address
->path
);
82 int ReadAll(HANDLE handle
, void* buffer
, size_t length
) {
84 while (count
< length
) {
86 DWORD chunk
= static_cast<DWORD
>(
87 ((length
- count
) <= UINT_MAX
) ? (length
- count
) : UINT_MAX
);
88 if (ReadFile(handle
, static_cast<char*>(buffer
) + count
,
89 chunk
, &len
, NULL
) == FALSE
) {
90 return static_cast<int>((0 < count
) ? count
: -1);
94 return static_cast<int>(count
);
97 int WriteAll(HANDLE handle
, const void* buffer
, size_t length
) {
99 while (count
< length
) {
101 // The following statement is for the 64 bit portability.
102 DWORD chunk
= static_cast<DWORD
>(
103 ((length
- count
) <= UINT_MAX
) ? (length
- count
) : UINT_MAX
);
104 if (WriteFile(handle
, static_cast<const char*>(buffer
) + count
,
105 chunk
, &len
, NULL
) == FALSE
) {
106 return static_cast<int>((0 < count
) ? count
: -1);
110 return static_cast<int>(count
);
113 BOOL
SkipFile(HANDLE handle
, size_t length
) {
116 size_t count
= std::min(sizeof scratch
, length
);
117 if (ReadAll(handle
, scratch
, count
) != count
) {
125 BOOL
SkipHandles(HANDLE handle
, size_t count
) {
128 if (ReadAll(handle
, &discard
, sizeof discard
) != sizeof discard
) {
131 CloseHandle(discard
);
140 return (GetLastError() == ERROR_PIPE_LISTENING
) ? true : false;
143 int GetLastErrorString(char* buffer
, size_t length
) {
144 DWORD error
= GetLastError();
145 return FormatMessageA(
146 FORMAT_MESSAGE_FROM_SYSTEM
|
147 FORMAT_MESSAGE_IGNORE_INSERTS
,
150 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
152 static_cast<DWORD
>((64 * 1024 < length
) ? 64 * 1024 : length
),
156 Handle
BoundSocket(const SocketAddress
* address
) {
157 char name
[kPipePathMax
];
158 if (!GetSocketName(address
, name
)) {
159 return kInvalidHandle
;
161 // Create a named pipe in nonblocking mode.
162 return CreateNamedPipeA(
164 PIPE_ACCESS_DUPLEX
| FILE_FLAG_FIRST_PIPE_INSTANCE
,
165 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_NOWAIT
,
166 PIPE_UNLIMITED_INSTANCES
,
169 kDefaultTimeoutMilliSeconds
,
173 int SocketPair(Handle pair
[2]) {
174 static AtomicWord socket_pair_count
;
176 char name
[kPipePathMax
];
178 sprintf_s(name
, kPipePathMax
, "%s%u.%lu",
179 kPipePrefix
, GetCurrentProcessId(),
180 AtomicIncrement(&socket_pair_count
, 1));
181 pair
[0] = CreateNamedPipeA(
183 PIPE_ACCESS_DUPLEX
| FILE_FLAG_FIRST_PIPE_INSTANCE
,
184 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
,
188 kDefaultTimeoutMilliSeconds
,
190 if (pair
[0] == INVALID_HANDLE_VALUE
&&
191 GetLastError() != ERROR_ACCESS_DENIED
&&
192 GetLastError() != ERROR_PIPE_BUSY
) {
195 } while (pair
[0] == INVALID_HANDLE_VALUE
);
196 pair
[1] = CreateFileA(name
,
197 GENERIC_READ
| GENERIC_WRITE
,
199 NULL
, // default security attributes
200 OPEN_EXISTING
, // opens existing pipe
201 0, // default attributes
202 NULL
); // no template file
203 if (pair
[1] == INVALID_HANDLE_VALUE
) {
204 CloseHandle(pair
[0]);
207 if (ConnectNamedPipe(pair
[0], NULL
) == FALSE
) {
208 DWORD error
= GetLastError();
209 if (error
!= ERROR_PIPE_CONNECTED
) {
210 CloseHandle(pair
[0]);
211 CloseHandle(pair
[1]);
218 int Close(Handle handle
) {
219 if (handle
== NULL
|| handle
== INVALID_HANDLE_VALUE
) {
222 return CloseHandle(handle
) ? 0 : -1;
225 int SendDatagram(Handle handle
, const MessageHeader
* message
, int flags
) {
226 ControlHeader header
= { kEchoRequest
, GetCurrentProcessId(), 0, 0 };
227 HANDLE remote_handles
[kHandleCountMax
];
229 if (kHandleCountMax
< message
->handle_count
) {
230 SetLastError(ERROR_INVALID_PARAMETER
);
233 if (0 < message
->handle_count
&& message
->handles
) {
234 // TODO: On Windows Vista, we can use GetNamedPipeClientProcessId()
235 // and GetNamedPipeServerProcessId() and probably we can remove
236 // kEchoRequest and kEchoResponse completely.
237 if (WriteAll(handle
, &header
, sizeof header
) != sizeof header
||
238 ReadAll(handle
, &header
, sizeof header
) != sizeof header
||
239 header
.command
!= kEchoResponse
) {
242 HANDLE target
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, header
.pid
);
243 if (target
== NULL
) {
246 for (size_t i
= 0; i
< message
->handle_count
; ++i
) {
247 if (DuplicateHandle(GetCurrentProcess(), message
->handles
[i
],
248 target
, &remote_handles
[i
],
249 0, FALSE
, DUPLICATE_SAME_ACCESS
) == FALSE
) {
250 // Send the kCancel message to revoke the handles duplicated
251 // so far in the remote peer.
252 header
.command
= kCancel
;
253 header
.handle_count
= i
;
255 WriteAll(handle
, &header
, sizeof header
);
256 WriteAll(handle
, remote_handles
, sizeof(Handle
) * i
);
264 header
.command
= kMessage
;
265 header
.handle_count
= message
->handle_count
;
266 for (size_t i
= 0; i
< message
->iov_length
; ++i
) {
267 header
.message_length
+= message
->iov
[i
].length
;
269 if (WriteAll(handle
, &header
, sizeof header
) != sizeof header
) {
272 for (size_t i
= 0; i
< message
->iov_length
; ++i
) {
273 if (WriteAll(handle
, message
->iov
[i
].base
, message
->iov
[i
].length
) !=
274 message
->iov
[i
].length
) {
278 if (0 < message
->handle_count
&& message
->handles
&&
281 sizeof(Handle
) * message
->handle_count
) !=
282 sizeof(Handle
) * message
->handle_count
) {
285 return static_cast<int>(header
.message_length
);
288 int SendDatagramTo(Handle handle
, const MessageHeader
* message
, int flags
,
289 const SocketAddress
* name
) {
290 if (kHandleCountMax
< message
->handle_count
) {
291 SetLastError(ERROR_INVALID_PARAMETER
);
294 char pipe_name
[kPipePathMax
];
295 if (!GetSocketName(name
, pipe_name
)) {
299 handle
= CreateFileA(pipe_name
,
300 GENERIC_READ
| GENERIC_WRITE
,
302 NULL
, // default security attributes
303 OPEN_EXISTING
, // opens existing pipe
304 0, // default attributes
305 NULL
); // no template file
306 if (handle
!= INVALID_HANDLE_VALUE
) {
309 if (GetLastError() != ERROR_PIPE_BUSY
) {
312 if (flags
& kDontWait
) {
313 SetLastError(ERROR_PIPE_LISTENING
);
316 if (!WaitNamedPipeA(pipe_name
, NMPWAIT_WAIT_FOREVER
)) {
320 int result
= SendDatagram(handle
, message
, flags
);
327 int ReceiveDatagram(Handle handle
, MessageHeader
* message
, int flags
,
329 ControlHeader header
;
331 bool dontPeek
= false;
333 if ((flags
& kDontWait
) && !dontPeek
) {
336 if (PeekNamedPipe(handle
, &header
, sizeof header
, &len
, &total
, NULL
)) {
337 if (len
< sizeof header
) {
338 SetLastError(ERROR_PIPE_LISTENING
);
340 switch (header
.command
) {
342 // Send back the process id to the remote peer to duplicate handles.
343 // TODO : It might be better to keep remote pid by the initial
344 // handshake rather than send kEchoRequest each time
345 // before duplicating handles.
346 if (ReadAll(handle
, &header
, sizeof header
) == sizeof header
) {
347 header
.command
= kEchoResponse
;
348 header
.pid
= GetCurrentProcessId();
349 WriteAll(handle
, &header
, sizeof header
);
351 // We must not close this connection.
358 SkipFile(handle
, sizeof header
);
362 if (header
.message_length
+ sizeof header
<= total
) {
363 if (flags
& kDontWait
) {
367 result
= static_cast<int>(header
.message_length
);
369 SetLastError(ERROR_PIPE_LISTENING
);
373 if (sizeof header
+ sizeof(Handle
) * header
.handle_count
<= len
&&
374 ReadAll(handle
, &header
, sizeof header
) == sizeof header
) {
375 SkipHandles(handle
, header
.handle_count
);
384 } else if (ReadAll(handle
, &header
, sizeof header
) == sizeof header
) {
386 switch (header
.command
) {
388 header
.command
= kEchoResponse
;
389 header
.pid
= GetCurrentProcessId();
390 WriteAll(handle
, &header
, sizeof header
);
397 size_t total_message_bytes
= header
.message_length
;
401 i
< message
->iov_length
&& count
< header
.message_length
;
403 IOVec
* iov
= &message
->iov
[i
];
404 size_t len
= std::min(iov
->length
, total_message_bytes
);
405 if (ReadAll(handle
, iov
->base
, len
) != len
) {
408 total_message_bytes
-= len
;
411 if (count
< header
.message_length
) {
412 if (SkipFile(handle
, header
.message_length
- count
) == FALSE
) {
415 message
->flags
|= kMessageTruncated
;
417 if (0 < message
->handle_count
&& message
->handles
) {
418 message
->handle_count
= std::min(message
->handle_count
,
419 header
.handle_count
);
420 if (ReadAll(handle
, message
->handles
,
421 message
->handle_count
* sizeof(Handle
)) !=
422 message
->handle_count
* sizeof(Handle
)) {
426 message
->handle_count
= 0;
428 if (message
->handle_count
< header
.handle_count
) {
429 if (SkipHandles(handle
, header
.handle_count
- message
->handle_count
) ==
433 message
->flags
|= kHandlesTruncated
;
435 result
= static_cast<int>(count
);
439 SkipHandles(handle
, header
.handle_count
);
451 int ReceiveDatagram(Handle handle
, MessageHeader
* message
, int flags
) {
452 // If handle is a bound socket, it is a named pipe in non-blocking mode.
453 // Set is_bound_socket to true if handle has been created by BoundSocket().
455 if (!GetNamedPipeHandleState(handle
, &state
, NULL
, NULL
, NULL
, NULL
, NULL
)) {
459 if (!(state
& PIPE_NOWAIT
)) {
460 // handle is a connected socket.
461 return ReceiveDatagram(handle
, message
, flags
, false);
464 // handle is a bound socket.
466 if (ConnectNamedPipe(handle
, NULL
)) {
467 // Note ConnectNamedPipe() for a handle in non-blocking mode returns a
468 // nonzero value just to indicate that the pipe is now available to be
472 switch (GetLastError()) {
473 case ERROR_PIPE_LISTENING
: {
474 if (flags
& kDontWait
) {
477 // Set handle to blocking mode
478 DWORD mode
= PIPE_READMODE_BYTE
| PIPE_WAIT
;
479 SetNamedPipeHandleState(handle
, &mode
, NULL
, NULL
);
482 case ERROR_PIPE_CONNECTED
: {
483 // Set handle to blocking mode
484 DWORD mode
= PIPE_READMODE_BYTE
| PIPE_WAIT
;
485 SetNamedPipeHandleState(handle
, &mode
, NULL
, NULL
);
486 int result
= ReceiveDatagram(handle
, message
, flags
, true);
487 FlushFileBuffers(handle
);
488 // Set handle back to non-blocking mode
489 mode
= PIPE_READMODE_BYTE
| PIPE_NOWAIT
;
490 SetNamedPipeHandleState(handle
, &mode
, NULL
, NULL
);
491 DisconnectNamedPipe(handle
);
492 if (result
== -1 && GetLastError() == ERROR_BROKEN_PIPE
) {
493 if (flags
& kDontWait
) {
494 SetLastError(ERROR_PIPE_LISTENING
);