]>
Commit | Line | Data |
---|---|---|
282113ba | 1 | /* transport_pipes.cc |
f449bfef | 2 | |
b079a89e | 3 | Copyright 2001, 2002, 2003, 2004, 2009 Red Hat Inc. |
f449bfef RC |
4 | |
5 | Written by Robert Collins <rbtcollins@hotmail.com> | |
6 | ||
1c001dd2 | 7 | This file is part of Cygwin. |
f449bfef | 8 | |
1c001dd2 CS |
9 | This software is a copyrighted work licensed under the terms of the |
10 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
11 | details. */ | |
12 | ||
13 | /* to allow this to link into cygwin and the .dll, a little magic is needed. */ | |
14 | #ifdef __OUTSIDE_CYGWIN__ | |
15 | #include "woutsup.h" | |
16 | #else | |
17 | #include "winsup.h" | |
18 | #endif | |
f449bfef | 19 | |
f449bfef | 20 | #include <sys/types.h> |
1c001dd2 CS |
21 | |
22 | #include <assert.h> | |
f449bfef | 23 | #include <netdb.h> |
1c001dd2 CS |
24 | #include <pthread.h> |
25 | #include <unistd.h> | |
b079a89e CV |
26 | #include <wchar.h> |
27 | #include <sys/cygwin.h> | |
1c001dd2 CS |
28 | |
29 | #include "cygerrno.h" | |
282113ba CV |
30 | #include "transport.h" |
31 | #include "transport_pipes.h" | |
f449bfef | 32 | |
1c001dd2 | 33 | #ifndef __INSIDE_CYGWIN__ |
56797078 | 34 | #include "cygserver.h" |
282113ba CV |
35 | #include "cygserver_ipc.h" |
36 | #else | |
37 | #include "security.h" | |
f449bfef RC |
38 | #endif |
39 | ||
1dcd520b CV |
40 | #ifdef __INSIDE_CYGWIN__ |
41 | #define SET_ERRNO(err) set_errno (err) | |
42 | #else | |
43 | #define SET_ERRNO(err) errno = (err) | |
44 | #endif | |
45 | ||
1c001dd2 CS |
46 | enum |
47 | { | |
48 | MAX_WAIT_NAMED_PIPE_RETRY = 64, | |
49 | WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds | |
50 | }; | |
51 | ||
52 | #ifndef __INSIDE_CYGWIN__ | |
53 | ||
54 | static pthread_once_t pipe_instance_lock_once = PTHREAD_ONCE_INIT; | |
55 | static CRITICAL_SECTION pipe_instance_lock; | |
56 | static long pipe_instance = 0; | |
57 | ||
58 | static void | |
59 | initialise_pipe_instance_lock () | |
60 | { | |
61 | assert (pipe_instance == 0); | |
62 | InitializeCriticalSection (&pipe_instance_lock); | |
63 | } | |
64 | ||
65 | #endif /* !__INSIDE_CYGWIN__ */ | |
66 | ||
67 | #ifndef __INSIDE_CYGWIN__ | |
241a7c5a | 68 | |
1c001dd2 | 69 | transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe) |
b079a89e | 70 | : _hPipe (hPipe), |
1c001dd2 CS |
71 | _is_accepted_endpoint (true), |
72 | _is_listening_endpoint (false) | |
f449bfef | 73 | { |
1c001dd2 CS |
74 | assert (_hPipe); |
75 | assert (_hPipe != INVALID_HANDLE_VALUE); | |
b079a89e | 76 | _pipe_name[0] = L'\0'; |
1c001dd2 CS |
77 | } |
78 | ||
79 | #endif /* !__INSIDE_CYGWIN__ */ | |
f449bfef | 80 | |
73ea29f4 | 81 | transport_layer_pipes::transport_layer_pipes () |
b079a89e | 82 | : _hPipe (NULL), |
1c001dd2 CS |
83 | _is_accepted_endpoint (false), |
84 | _is_listening_endpoint (false) | |
f449bfef | 85 | { |
b079a89e CV |
86 | #ifdef __INSIDE_CYGWIN__ |
87 | extern WCHAR installation_key_buf[18]; | |
88 | wcpcpy (wcpcpy (wcpcpy (_pipe_name, PIPE_NAME_PREFIX), installation_key_buf), | |
89 | PIPE_NAME_SUFFIX); | |
90 | #else | |
91 | wchar_t cyg_instkey[18]; | |
92 | ||
93 | wchar_t *p = wcpcpy (_pipe_name, PIPE_NAME_PREFIX); | |
1636ce3b | 94 | if (!cygwin_internal (CW_GET_INSTKEY, cyg_instkey)) |
b079a89e CV |
95 | wcpcpy (wcpcpy (p, cyg_instkey), PIPE_NAME_SUFFIX); |
96 | #endif | |
f449bfef RC |
97 | } |
98 | ||
1c001dd2 CS |
99 | transport_layer_pipes::~transport_layer_pipes () |
100 | { | |
101 | close (); | |
102 | } | |
103 | ||
104 | #ifndef __INSIDE_CYGWIN__ | |
105 | ||
106 | int | |
f449bfef RC |
107 | transport_layer_pipes::listen () |
108 | { | |
1c001dd2 CS |
109 | assert (!_hPipe); |
110 | assert (!_is_accepted_endpoint); | |
111 | assert (!_is_listening_endpoint); | |
112 | ||
113 | _is_listening_endpoint = true; | |
114 | ||
f449bfef | 115 | /* no-op */ |
1c001dd2 | 116 | return 0; |
f449bfef RC |
117 | } |
118 | ||
119 | class transport_layer_pipes * | |
1c001dd2 | 120 | transport_layer_pipes::accept (bool *const recoverable) |
f449bfef | 121 | { |
1c001dd2 CS |
122 | assert (!_hPipe); |
123 | assert (!_is_accepted_endpoint); | |
124 | assert (_is_listening_endpoint); | |
125 | ||
126 | pthread_once (&pipe_instance_lock_once, &initialise_pipe_instance_lock); | |
127 | ||
128 | EnterCriticalSection (&pipe_instance_lock); | |
129 | ||
130 | // Read: http://www.securityinternals.com/research/papers/namedpipe.php | |
131 | // See also the Microsoft security bulletins MS00-053 and MS01-031. | |
132 | ||
133 | // FIXME: Remove FILE_CREATE_PIPE_INSTANCE. | |
134 | ||
135 | const bool first_instance = (pipe_instance == 0); | |
136 | ||
1636ce3b CV |
137 | debug ("Try to create named pipe: %ls", _pipe_name); |
138 | ||
1c001dd2 | 139 | const HANDLE accept_pipe = |
b079a89e | 140 | CreateNamedPipeW (_pipe_name, |
1c001dd2 CS |
141 | (PIPE_ACCESS_DUPLEX |
142 | | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0)), | |
143 | (PIPE_TYPE_BYTE | PIPE_WAIT), | |
144 | PIPE_UNLIMITED_INSTANCES, | |
145 | 0, 0, 1000, | |
282113ba | 146 | &sec_all_nih); |
1c001dd2 CS |
147 | |
148 | const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE | |
149 | && pipe_instance == 0 | |
150 | && GetLastError () == ERROR_ACCESS_DENIED); | |
151 | ||
152 | if (accept_pipe != INVALID_HANDLE_VALUE) | |
153 | InterlockedIncrement (&pipe_instance); | |
154 | ||
155 | LeaveCriticalSection (&pipe_instance_lock); | |
156 | ||
157 | if (duplicate) | |
f449bfef | 158 | { |
1c001dd2 CS |
159 | *recoverable = false; |
160 | system_printf ("failed to create named pipe: " | |
161 | "is the daemon already running?"); | |
f449bfef RC |
162 | return NULL; |
163 | } | |
164 | ||
1c001dd2 | 165 | if (accept_pipe == INVALID_HANDLE_VALUE) |
f449bfef | 166 | { |
1c001dd2 CS |
167 | debug_printf ("error creating pipe (%lu).", GetLastError ()); |
168 | *recoverable = true; // FIXME: case analysis? | |
f449bfef RC |
169 | return NULL; |
170 | } | |
171 | ||
1c001dd2 CS |
172 | assert (accept_pipe); |
173 | ||
174 | if (!ConnectNamedPipe (accept_pipe, NULL) | |
175 | && GetLastError () != ERROR_PIPE_CONNECTED) | |
f449bfef | 176 | { |
18be975d | 177 | debug_printf ("error connecting to pipe (%lu)", GetLastError ()); |
1c001dd2 CS |
178 | (void) CloseHandle (accept_pipe); |
179 | *recoverable = true; // FIXME: case analysis? | |
f449bfef RC |
180 | return NULL; |
181 | } | |
73ea29f4 | 182 | |
282113ba | 183 | return new transport_layer_pipes (accept_pipe); |
f449bfef RC |
184 | } |
185 | ||
1c001dd2 CS |
186 | #endif /* !__INSIDE_CYGWIN__ */ |
187 | ||
f449bfef | 188 | void |
1c001dd2 | 189 | transport_layer_pipes::close () |
f449bfef | 190 | { |
1c001dd2 CS |
191 | // verbose: debug_printf ("closing pipe %p", _hPipe); |
192 | ||
193 | if (_hPipe) | |
f449bfef | 194 | { |
1c001dd2 CS |
195 | assert (_hPipe != INVALID_HANDLE_VALUE); |
196 | ||
197 | #ifndef __INSIDE_CYGWIN__ | |
198 | ||
199 | if (_is_accepted_endpoint) | |
200 | { | |
201 | (void) FlushFileBuffers (_hPipe); // Blocks until client reads. | |
202 | (void) DisconnectNamedPipe (_hPipe); | |
203 | EnterCriticalSection (&pipe_instance_lock); | |
204 | (void) CloseHandle (_hPipe); | |
205 | assert (pipe_instance > 0); | |
206 | InterlockedDecrement (&pipe_instance); | |
207 | LeaveCriticalSection (&pipe_instance_lock); | |
208 | } | |
209 | else | |
210 | (void) CloseHandle (_hPipe); | |
211 | ||
212 | #else /* __INSIDE_CYGWIN__ */ | |
213 | ||
214 | assert (!_is_accepted_endpoint); | |
215 | (void) ForceCloseHandle (_hPipe); | |
216 | ||
217 | #endif /* __INSIDE_CYGWIN__ */ | |
218 | ||
219 | _hPipe = NULL; | |
f449bfef RC |
220 | } |
221 | } | |
222 | ||
223 | ssize_t | |
1c001dd2 | 224 | transport_layer_pipes::read (void *const buf, const size_t len) |
f449bfef | 225 | { |
1c001dd2 | 226 | // verbose: debug_printf ("reading from pipe %p", _hPipe); |
f449bfef | 227 | |
1c001dd2 CS |
228 | assert (_hPipe); |
229 | assert (_hPipe != INVALID_HANDLE_VALUE); | |
230 | assert (!_is_listening_endpoint); | |
231 | ||
232 | DWORD count; | |
233 | if (!ReadFile (_hPipe, buf, len, &count, NULL)) | |
f449bfef | 234 | { |
1c001dd2 | 235 | debug_printf ("error reading from pipe (%lu)", GetLastError ()); |
1dcd520b | 236 | SET_ERRNO (EINVAL); // FIXME? |
f449bfef RC |
237 | return -1; |
238 | } | |
1c001dd2 CS |
239 | |
240 | return count; | |
f449bfef RC |
241 | } |
242 | ||
243 | ssize_t | |
1c001dd2 | 244 | transport_layer_pipes::write (void *const buf, const size_t len) |
f449bfef | 245 | { |
1c001dd2 | 246 | // verbose: debug_printf ("writing to pipe %p", _hPipe); |
f449bfef | 247 | |
1c001dd2 CS |
248 | assert (_hPipe); |
249 | assert (_hPipe != INVALID_HANDLE_VALUE); | |
250 | assert (!_is_listening_endpoint); | |
251 | ||
252 | DWORD count; | |
253 | if (!WriteFile (_hPipe, buf, len, &count, NULL)) | |
f449bfef | 254 | { |
1c001dd2 | 255 | debug_printf ("error writing to pipe, error = %lu", GetLastError ()); |
1dcd520b | 256 | SET_ERRNO (EINVAL); // FIXME? |
f449bfef RC |
257 | return -1; |
258 | } | |
1c001dd2 CS |
259 | |
260 | return count; | |
f449bfef RC |
261 | } |
262 | ||
1c001dd2 CS |
263 | /* |
264 | * This routine holds a static variable, assume_cygserver, that is set | |
265 | * if the transport has good reason to think that cygserver is | |
266 | * running, i.e. if if successfully connected to it with the previous | |
267 | * attempt. If this is set, the code tries a lot harder to get a | |
268 | * connection, making the assumption that any failures are just | |
269 | * congestion and overloading problems. | |
270 | */ | |
271 | ||
272 | int | |
f449bfef RC |
273 | transport_layer_pipes::connect () |
274 | { | |
1c001dd2 CS |
275 | assert (!_hPipe); |
276 | assert (!_is_accepted_endpoint); | |
277 | assert (!_is_listening_endpoint); | |
f449bfef | 278 | |
1c001dd2 CS |
279 | static bool assume_cygserver = false; |
280 | ||
281 | BOOL rc = TRUE; | |
282 | int retries = 0; | |
283 | ||
1636ce3b | 284 | debug_printf ("Try to connect to named pipe: %W", _pipe_name); |
1c001dd2 | 285 | while (rc) |
f449bfef | 286 | { |
b079a89e CV |
287 | _hPipe = CreateFileW (_pipe_name, |
288 | GENERIC_READ | GENERIC_WRITE, | |
289 | FILE_SHARE_READ | FILE_SHARE_WRITE, | |
290 | &sec_all_nih, | |
291 | OPEN_EXISTING, | |
292 | SECURITY_IMPERSONATION, | |
293 | NULL); | |
1c001dd2 CS |
294 | |
295 | if (_hPipe != INVALID_HANDLE_VALUE) | |
296 | { | |
297 | assert (_hPipe); | |
298 | #ifdef __INSIDE_CYGWIN__ | |
299 | ProtectHandle (_hPipe); | |
300 | #endif | |
301 | assume_cygserver = true; | |
302 | return 0; | |
303 | } | |
304 | ||
305 | _hPipe = NULL; | |
306 | ||
307 | if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY) | |
73ea29f4 | 308 | { |
1c001dd2 CS |
309 | debug_printf ("Error opening the pipe (%lu)", GetLastError ()); |
310 | return -1; | |
73ea29f4 | 311 | } |
1c001dd2 CS |
312 | |
313 | /* Note: `If no instances of the specified named pipe exist, the | |
314 | * WaitNamedPipe function returns immediately, regardless of the | |
315 | * time-out value.' Thus the explicit Sleep if the call fails | |
316 | * with ERROR_FILE_NOT_FOUND. | |
73ea29f4 | 317 | */ |
1c001dd2 | 318 | while (retries != MAX_WAIT_NAMED_PIPE_RETRY |
b079a89e | 319 | && !(rc = WaitNamedPipeW (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT))) |
1c001dd2 CS |
320 | { |
321 | if (GetLastError () == ERROR_FILE_NOT_FOUND) | |
322 | Sleep (0); // Give the server a chance. | |
323 | ||
324 | retries += 1; | |
325 | } | |
f449bfef | 326 | } |
1c001dd2 CS |
327 | |
328 | assert (retries == MAX_WAIT_NAMED_PIPE_RETRY); | |
329 | ||
330 | system_printf ("lost connection to cygserver, error = %lu", | |
331 | GetLastError ()); | |
332 | ||
333 | assume_cygserver = false; | |
334 | ||
335 | return -1; | |
f449bfef RC |
336 | } |
337 | ||
1c001dd2 CS |
338 | #ifndef __INSIDE_CYGWIN__ |
339 | ||
282113ba | 340 | bool |
f449bfef RC |
341 | transport_layer_pipes::impersonate_client () |
342 | { | |
1c001dd2 CS |
343 | assert (_hPipe); |
344 | assert (_hPipe != INVALID_HANDLE_VALUE); | |
345 | assert (_is_accepted_endpoint); | |
346 | ||
282113ba | 347 | if (_hPipe && !ImpersonateNamedPipeClient (_hPipe)) |
f449bfef | 348 | { |
282113ba CV |
349 | debug_printf ("Failed to Impersonate client, (%lu)", GetLastError ()); |
350 | return false; | |
f449bfef | 351 | } |
282113ba CV |
352 | |
353 | return true; | |
f449bfef RC |
354 | } |
355 | ||
282113ba | 356 | bool |
f449bfef RC |
357 | transport_layer_pipes::revert_to_self () |
358 | { | |
1c001dd2 CS |
359 | assert (_is_accepted_endpoint); |
360 | ||
282113ba CV |
361 | if (!RevertToSelf ()) |
362 | { | |
363 | debug_printf ("Failed to RevertToSelf, (%lu)", GetLastError ()); | |
364 | return false; | |
365 | } | |
366 | return true; | |
f449bfef RC |
367 | } |
368 | ||
1c001dd2 | 369 | #endif /* !__INSIDE_CYGWIN__ */ |