[email protected] | 774bdcce | 2012-01-19 03:44:38 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "base/sync_socket.h" |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 6 | |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 7 | #include "base/logging.h" |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 8 | #include "base/win/scoped_handle.h" |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 9 | |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 10 | namespace base { |
| 11 | |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 12 | using win::ScopedHandle; |
| 13 | |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 14 | namespace { |
[email protected] | 774bdcce | 2012-01-19 03:44:38 | [diff] [blame] | 15 | // IMPORTANT: do not change how this name is generated because it will break |
| 16 | // in sandboxed scenarios as we might have by-name policies that allow pipe |
| 17 | // creation. Also keep the secure random number generation. |
| 18 | const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\chrome.sync.%u.%u.%lu"; |
| 19 | const size_t kPipePathMax = arraysize(kPipeNameFormat) + (3 * 10) + 1; |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 20 | |
| 21 | // To avoid users sending negative message lengths to Send/Receive |
| 22 | // we clamp message lengths, which are size_t, to no more than INT_MAX. |
| 23 | const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX); |
| 24 | |
| 25 | const int kOutBufferSize = 4096; |
| 26 | const int kInBufferSize = 4096; |
| 27 | const int kDefaultTimeoutMilliSeconds = 1000; |
| 28 | |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 29 | bool CreatePairImpl(HANDLE* socket_a, HANDLE* socket_b, bool overlapped) { |
| 30 | DCHECK(socket_a != socket_b); |
| 31 | DCHECK(*socket_a == SyncSocket::kInvalidHandle); |
| 32 | DCHECK(*socket_b == SyncSocket::kInvalidHandle); |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 33 | |
| 34 | wchar_t name[kPipePathMax]; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 35 | ScopedHandle handle_a; |
| 36 | DWORD flags = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE; |
| 37 | if (overlapped) |
| 38 | flags |= FILE_FLAG_OVERLAPPED; |
| 39 | |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 40 | do { |
| 41 | unsigned int rnd_name; |
| 42 | if (rand_s(&rnd_name) != 0) |
| 43 | return false; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 44 | |
[email protected] | 774bdcce | 2012-01-19 03:44:38 | [diff] [blame] | 45 | swprintf(name, kPipePathMax, |
| 46 | kPipeNameFormat, |
| 47 | GetCurrentProcessId(), |
| 48 | GetCurrentThreadId(), |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 49 | rnd_name); |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 50 | |
| 51 | handle_a.Set(CreateNamedPipeW( |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 52 | name, |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 53 | flags, |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 54 | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, |
| 55 | 1, |
| 56 | kOutBufferSize, |
| 57 | kInBufferSize, |
| 58 | kDefaultTimeoutMilliSeconds, |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 59 | NULL)); |
| 60 | } while (!handle_a.IsValid() && |
[email protected] | 774bdcce | 2012-01-19 03:44:38 | [diff] [blame] | 61 | (GetLastError() == ERROR_PIPE_BUSY)); |
| 62 | |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 63 | if (!handle_a.IsValid()) { |
[email protected] | 774bdcce | 2012-01-19 03:44:38 | [diff] [blame] | 64 | NOTREACHED(); |
| 65 | return false; |
| 66 | } |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 67 | |
| 68 | // The SECURITY_ANONYMOUS flag means that the server side (handle_a) cannot |
| 69 | // impersonate the client (handle_b). This allows us not to care which side |
[email protected] | 774bdcce | 2012-01-19 03:44:38 | [diff] [blame] | 70 | // ends up in which side of a privilege boundary. |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 71 | flags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS; |
| 72 | if (overlapped) |
| 73 | flags |= FILE_FLAG_OVERLAPPED; |
| 74 | |
| 75 | ScopedHandle handle_b(CreateFileW(name, |
| 76 | GENERIC_READ | GENERIC_WRITE, |
| 77 | 0, // no sharing. |
| 78 | NULL, // default security attributes. |
| 79 | OPEN_EXISTING, // opens existing pipe. |
| 80 | flags, |
| 81 | NULL)); // no template file. |
| 82 | if (!handle_b.IsValid()) { |
| 83 | DPLOG(ERROR) << "CreateFileW failed"; |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 84 | return false; |
| 85 | } |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 86 | |
| 87 | if (!ConnectNamedPipe(handle_a, NULL)) { |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 88 | DWORD error = GetLastError(); |
| 89 | if (error != ERROR_PIPE_CONNECTED) { |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 90 | DPLOG(ERROR) << "ConnectNamedPipe failed"; |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 91 | return false; |
| 92 | } |
| 93 | } |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 94 | |
| 95 | *socket_a = handle_a.Take(); |
| 96 | *socket_b = handle_b.Take(); |
| 97 | |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 98 | return true; |
| 99 | } |
| 100 | |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 101 | // Inline helper to avoid having the cast everywhere. |
| 102 | DWORD GetNextChunkSize(size_t current_pos, size_t max_size) { |
| 103 | // The following statement is for 64 bit portability. |
| 104 | return static_cast<DWORD>(((max_size - current_pos) <= UINT_MAX) ? |
| 105 | (max_size - current_pos) : UINT_MAX); |
| 106 | } |
| 107 | |
| 108 | // Template function that supports calling ReadFile or WriteFile in an |
| 109 | // overlapped fashion and waits for IO completion. The function also waits |
| 110 | // on an event that can be used to cancel the operation. If the operation |
| 111 | // is cancelled, the function returns and closes the relevant socket object. |
| 112 | template <typename BufferType, typename Function> |
| 113 | size_t CancelableFileOperation(Function operation, HANDLE file, |
| 114 | BufferType* buffer, size_t length, |
| 115 | base::WaitableEvent* io_event, |
| 116 | base::WaitableEvent* cancel_event, |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 117 | CancelableSyncSocket* socket, |
| 118 | DWORD timeout_in_ms) { |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 119 | // The buffer must be byte size or the length check won't make much sense. |
| 120 | COMPILE_ASSERT(sizeof(buffer[0]) == sizeof(char), incorrect_buffer_type); |
| 121 | DCHECK_LE(length, kMaxMessageLength); |
| 122 | |
| 123 | OVERLAPPED ol = {0}; |
| 124 | ol.hEvent = io_event->handle(); |
| 125 | size_t count = 0; |
| 126 | while (count < length) { |
| 127 | DWORD chunk = GetNextChunkSize(count, length); |
| 128 | // This is either the ReadFile or WriteFile call depending on whether |
| 129 | // we're receiving or sending data. |
[email protected] | 23bf698 | 2012-02-03 12:44:44 | [diff] [blame] | 130 | DWORD len = 0; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 131 | BOOL ok = operation(file, static_cast<BufferType*>(buffer) + count, chunk, |
| 132 | &len, &ol); |
| 133 | if (!ok) { |
| 134 | if (::GetLastError() == ERROR_IO_PENDING) { |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 135 | HANDLE events[] = { io_event->handle(), cancel_event->handle() }; |
| 136 | int wait_result = WaitForMultipleObjects( |
| 137 | arraysize(events), events, FALSE, timeout_in_ms); |
| 138 | if (wait_result == (WAIT_OBJECT_0 + 0)) { |
| 139 | GetOverlappedResult(file, &ol, &len, TRUE); |
| 140 | } else if (wait_result == (WAIT_OBJECT_0 + 1)) { |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 141 | VLOG(1) << "Shutdown was signaled. Closing socket."; |
[email protected] | 23bf698 | 2012-02-03 12:44:44 | [diff] [blame] | 142 | CancelIo(file); |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 143 | socket->Close(); |
[email protected] | 23bf698 | 2012-02-03 12:44:44 | [diff] [blame] | 144 | count = 0; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 145 | break; |
| 146 | } else { |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 147 | // Timeout happened. |
| 148 | DCHECK_EQ(WAIT_TIMEOUT, wait_result); |
| 149 | if (!CancelIo(file)){ |
| 150 | DLOG(WARNING) << "CancelIo() failed"; |
| 151 | } |
| 152 | break; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 153 | } |
| 154 | } else { |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 155 | break; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 156 | } |
| 157 | } |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 158 | |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 159 | count += len; |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 160 | |
| 161 | // Quit the operation if we can't write/read anymore. |
| 162 | if (len != chunk) |
| 163 | break; |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 164 | } |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 165 | |
| 166 | return (count > 0) ? count : 0; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | } // namespace |
| 170 | |
| 171 | const SyncSocket::Handle SyncSocket::kInvalidHandle = INVALID_HANDLE_VALUE; |
| 172 | |
| 173 | SyncSocket::SyncSocket() : handle_(kInvalidHandle) {} |
| 174 | |
| 175 | SyncSocket::~SyncSocket() { |
| 176 | Close(); |
| 177 | } |
| 178 | |
| 179 | // static |
| 180 | bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) { |
| 181 | return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, false); |
| 182 | } |
| 183 | |
| 184 | bool SyncSocket::Close() { |
| 185 | if (handle_ == kInvalidHandle) |
| 186 | return false; |
| 187 | |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 188 | BOOL retval = CloseHandle(handle_); |
| 189 | handle_ = kInvalidHandle; |
| 190 | return retval ? true : false; |
| 191 | } |
| 192 | |
| 193 | size_t SyncSocket::Send(const void* buffer, size_t length) { |
[email protected] | d7a93ad | 2011-04-22 13:13:07 | [diff] [blame] | 194 | DCHECK_LE(length, kMaxMessageLength); |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 195 | size_t count = 0; |
| 196 | while (count < length) { |
| 197 | DWORD len; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 198 | DWORD chunk = GetNextChunkSize(count, length); |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 199 | if (WriteFile(handle_, static_cast<const char*>(buffer) + count, |
| 200 | chunk, &len, NULL) == FALSE) { |
| 201 | return (0 < count) ? count : 0; |
| 202 | } |
| 203 | count += len; |
| 204 | } |
| 205 | return count; |
| 206 | } |
| 207 | |
| 208 | size_t SyncSocket::Receive(void* buffer, size_t length) { |
[email protected] | d7a93ad | 2011-04-22 13:13:07 | [diff] [blame] | 209 | DCHECK_LE(length, kMaxMessageLength); |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 210 | size_t count = 0; |
| 211 | while (count < length) { |
| 212 | DWORD len; |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 213 | DWORD chunk = GetNextChunkSize(count, length); |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 214 | if (ReadFile(handle_, static_cast<char*>(buffer) + count, |
| 215 | chunk, &len, NULL) == FALSE) { |
| 216 | return (0 < count) ? count : 0; |
| 217 | } |
| 218 | count += len; |
| 219 | } |
| 220 | return count; |
| 221 | } |
| 222 | |
[email protected] | d8b6591 | 2009-12-04 22:53:22 | [diff] [blame] | 223 | size_t SyncSocket::Peek() { |
| 224 | DWORD available = 0; |
| 225 | PeekNamedPipe(handle_, NULL, 0, NULL, &available, NULL); |
| 226 | return available; |
| 227 | } |
| 228 | |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 229 | CancelableSyncSocket::CancelableSyncSocket() |
| 230 | : shutdown_event_(true, false), file_operation_(true, false) { |
| 231 | } |
| 232 | |
| 233 | CancelableSyncSocket::CancelableSyncSocket(Handle handle) |
| 234 | : SyncSocket(handle), shutdown_event_(true, false), |
| 235 | file_operation_(true, false) { |
| 236 | } |
| 237 | |
| 238 | bool CancelableSyncSocket::Shutdown() { |
| 239 | // This doesn't shut down the pipe immediately, but subsequent Receive or Send |
| 240 | // methods will fail straight away. |
| 241 | shutdown_event_.Signal(); |
| 242 | return true; |
| 243 | } |
| 244 | |
| 245 | bool CancelableSyncSocket::Close() { |
| 246 | bool ret = SyncSocket::Close(); |
| 247 | shutdown_event_.Reset(); |
| 248 | return ret; |
| 249 | } |
| 250 | |
| 251 | size_t CancelableSyncSocket::Send(const void* buffer, size_t length) { |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 252 | static const DWORD kWaitTimeOutInMs = 500; |
| 253 | return CancelableFileOperation( |
| 254 | &WriteFile, handle_, reinterpret_cast<const char*>(buffer), |
| 255 | length, &file_operation_, &shutdown_event_, this, kWaitTimeOutInMs); |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 256 | } |
| 257 | |
| 258 | size_t CancelableSyncSocket::Receive(void* buffer, size_t length) { |
| 259 | return CancelableFileOperation(&ReadFile, handle_, |
| 260 | reinterpret_cast<char*>(buffer), length, &file_operation_, |
[email protected] | 5d27209 | 2012-04-19 10:23:03 | [diff] [blame^] | 261 | &shutdown_event_, this, INFINITE); |
[email protected] | 532e9bd | 2012-01-25 12:04:17 | [diff] [blame] | 262 | } |
| 263 | |
| 264 | // static |
| 265 | bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a, |
| 266 | CancelableSyncSocket* socket_b) { |
| 267 | return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, true); |
| 268 | } |
| 269 | |
| 270 | |
[email protected] | 0840cc7 | 2009-11-24 16:14:53 | [diff] [blame] | 271 | } // namespace base |