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