]> git.proxmox.com Git - mirror_ovs.git/blob - lib/stream-windows.c
netdev: Reject empty names in netdev_open().
[mirror_ovs.git] / lib / stream-windows.c
1 /*
2 * Copyright (c) 2016 Cloudbase Solutions Srl
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18 #include <errno.h>
19 #include <sys/types.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include "poll-loop.h"
24 #include "dirs.h"
25 #include "util.h"
26 #include "stream-provider.h"
27 #include "openvswitch/vlog.h"
28
29 VLOG_DEFINE_THIS_MODULE(stream_windows);
30
31 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
32
33 static void maybe_unlink_and_free(char *path);
34
35 /* Suggested buffer size at the creation of the named pipe for reading and
36 * and writing operations. */
37 #define BUFSIZE 65000
38
39 /* Default prefix of a local named pipe. */
40 #define LOCAL_PREFIX "\\\\.\\pipe\\"
41
42 /* This function has the purpose to remove all the slashes received in s. */
43 static char *
44 remove_slashes(char *s)
45 {
46 char *p1, *p2;
47 p1 = p2 = s;
48
49 while (*p1) {
50 if ((*p1) == '\\' || (*p1) == '/') {
51 p1++;
52 } else {
53 *p2 = *p1;
54 p2++;
55 p1++;
56 }
57 }
58 *p2 = '\0';
59 return s;
60 }
61
62 /* Active named pipe. */
63 struct windows_stream
64 {
65 struct stream stream;
66 HANDLE fd;
67 /* Overlapped operations used for reading/writing. */
68 OVERLAPPED read;
69 OVERLAPPED write;
70 /* Flag to check if a reading/writing operation is pending. */
71 bool read_pending;
72 bool write_pending;
73 /* Flag to check if fd is a server HANDLE. In the case of a server handle
74 * we have to issue a disconnect before closing the actual handle. */
75 bool server;
76 bool retry_connect;
77 char *pipe_path;
78 };
79
80 static struct windows_stream *
81 stream_windows_cast(struct stream *stream)
82 {
83 stream_assert_class(stream, &windows_stream_class);
84 return CONTAINER_OF(stream, struct windows_stream, stream);
85 }
86
87 static HANDLE
88 create_snpipe(char *path)
89 {
90 return CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
91 OPEN_EXISTING,
92 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED |
93 FILE_FLAG_NO_BUFFERING,
94 NULL);
95 }
96
97 /* Active named pipe open. */
98 static int
99 windows_open(const char *name, char *suffix, struct stream **streamp,
100 uint8_t dscp OVS_UNUSED)
101 {
102 char *connect_path;
103 HANDLE npipe;
104 DWORD mode = PIPE_READMODE_BYTE;
105 char *path;
106 FILE *file;
107 bool retry = false;
108 /* If the path does not contain a ':', assume it is relative to
109 * OVS_RUNDIR. */
110 if (!strchr(suffix, ':')) {
111 path = xasprintf("%s/%s", ovs_rundir(), suffix);
112 } else {
113 path = xstrdup(suffix);
114 }
115
116 /* In case of "unix:" argument, the assumption is that there is a file
117 * created in the path (name). */
118 file = fopen(path, "r");
119 if (!file) {
120 free(path);
121 VLOG_DBG_RL(&rl, "%s: could not open %s (%s)", name, suffix,
122 ovs_strerror(errno));
123 return ENOENT;
124 } else {
125 fclose(file);
126 }
127
128 /* Valid pipe names do not have slashes. The assumption is that the named
129 * pipe was created with the name "path", with slashes removed and the
130 * default prefix \\.\pipe\ appended.
131 * Strip the slashes from the parameter name and append the default prefix.
132 */
133 connect_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
134 free(path);
135
136 /* Try to connect to the named pipe. In case all pipe instances are
137 * busy we set the retry flag to true and retry again during the
138 * connect function. Use overlapped flag and file no buffering to ensure
139 * asynchronous operations. */
140 npipe = create_snpipe(connect_path);
141
142 if (npipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY) {
143 retry = true;
144 }
145
146 if (!retry && npipe == INVALID_HANDLE_VALUE) {
147 VLOG_ERR_RL(&rl, "Could not connect to named pipe: %s",
148 ovs_lasterror_to_string());
149 free(connect_path);
150 return ENOENT;
151 }
152 if (!retry && !SetNamedPipeHandleState(npipe, &mode, NULL, NULL)) {
153 VLOG_ERR_RL(&rl, "Could not set named pipe options: %s",
154 ovs_lasterror_to_string());
155 free(connect_path);
156 CloseHandle(npipe);
157 return ENOENT;
158 }
159 struct windows_stream *s = xmalloc(sizeof *s);
160 stream_init(&s->stream, &windows_stream_class, 0, name);
161 s->pipe_path = connect_path;
162 s->fd = npipe;
163 /* This is an active stream. */
164 s->server = false;
165 /* Create events for reading and writing to be signaled later. */
166 memset(&s->read, 0, sizeof(s->read));
167 memset(&s->write, 0, sizeof(s->write));
168 s->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
169 s->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
170 /* Initial read and write operations are not pending. */
171 s->read_pending = false;
172 s->write_pending = false;
173 s->retry_connect = retry;
174 *streamp = &s->stream;
175 return 0;
176 }
177
178 /* Active named pipe close. */
179 static void
180 windows_close(struct stream *stream)
181 {
182 struct windows_stream *s = stream_windows_cast(stream);
183 /* Disconnect the named pipe in case it was created from a passive stream.
184 */
185 if (s->server) {
186 /* Flush the pipe to allow the client to read the pipe's contents
187 * before disconnecting. */
188 FlushFileBuffers(s->fd);
189 DisconnectNamedPipe(s->fd);
190 }
191 CloseHandle(s->fd);
192 CloseHandle(s->read.hEvent);
193 CloseHandle(s->write.hEvent);
194 if (s->pipe_path) {
195 free(s->pipe_path);
196 }
197 free(s);
198 }
199
200 /* Active named pipe connect. */
201 static int
202 windows_connect(struct stream *stream)
203 {
204 struct windows_stream *s = stream_windows_cast(stream);
205
206 if (!s->retry_connect) {
207 return 0;
208 } else {
209 HANDLE npipe;
210 npipe = create_snpipe(s->pipe_path);
211 if (npipe == INVALID_HANDLE_VALUE) {
212 if (GetLastError() == ERROR_PIPE_BUSY) {
213 return EAGAIN;
214 } else {
215 s->retry_connect = false;
216 return ENOENT;
217 }
218 }
219 s->retry_connect = false;
220 s->fd = npipe;
221 return 0;
222 }
223 }
224
225 /* Active named pipe receive. */
226 static ssize_t
227 windows_recv(struct stream *stream, void *buffer, size_t n)
228 {
229 struct windows_stream *s = stream_windows_cast(stream);
230 ssize_t retval = 0;
231 boolean result = false;
232 DWORD last_error = 0;
233 LPOVERLAPPED ov = NULL;
234 ov = &s->read;
235
236 /* If the read operation was pending, we verify its result. */
237 if (s->read_pending) {
238 if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
239 last_error = GetLastError();
240 if (last_error == ERROR_IO_INCOMPLETE) {
241 /* If the operation is still pending, retry again. */
242 s->read_pending = true;
243 return -EAGAIN;
244 } else if (last_error == ERROR_PIPE_NOT_CONNECTED
245 || last_error == ERROR_BAD_PIPE
246 || last_error == ERROR_NO_DATA
247 || last_error == ERROR_BROKEN_PIPE) {
248 /* If the pipe was disconnected, return 0. */
249 return 0;
250 } else {
251 VLOG_ERR_RL(&rl, "Could not receive data on named pipe. Last "
252 "error: %s", ovs_lasterror_to_string());
253 return -EINVAL;
254 }
255 }
256 s->read_pending = false;
257 return retval;
258 }
259
260 result = ReadFile(s->fd, buffer, n, &(DWORD)retval, ov);
261
262 if (!result && GetLastError() == ERROR_IO_PENDING) {
263 /* Mark the read operation as pending. */
264 s->read_pending = true;
265 return -EAGAIN;
266 } else if (!result) {
267 last_error = GetLastError();
268 if (last_error == ERROR_PIPE_NOT_CONNECTED
269 || last_error == ERROR_BAD_PIPE
270 || last_error == ERROR_NO_DATA
271 || last_error == ERROR_BROKEN_PIPE) {
272 /* If the pipe was disconnected, return 0. */
273 return 0;
274 }
275 VLOG_ERR_RL(&rl, "Could not receive data synchronous on named pipe."
276 "Last error: %s", ovs_lasterror_to_string());
277 return -EINVAL;
278 }
279
280 return retval;
281 }
282
283 /* Active named pipe send. */
284 static ssize_t
285 windows_send(struct stream *stream, const void *buffer, size_t n)
286 {
287 struct windows_stream *s = stream_windows_cast(stream);
288 ssize_t retval = 0;
289 boolean result = false;
290 DWORD last_error = 0;
291 LPOVERLAPPED ov = NULL;
292 ov = &s->write;
293
294 /* If the send operation was pending, we verify the result. */
295 if (s->write_pending) {
296 if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
297 last_error = GetLastError();
298 if (last_error == ERROR_IO_INCOMPLETE) {
299 /* If the operation is still pending, retry again. */
300 s->write_pending = true;
301 return -EAGAIN;
302 } else if (last_error == ERROR_PIPE_NOT_CONNECTED
303 || last_error == ERROR_BAD_PIPE
304 || last_error == ERROR_NO_DATA
305 || last_error == ERROR_BROKEN_PIPE) {
306 /* If the pipe was disconnected, return connection reset. */
307 return -WSAECONNRESET;
308 } else {
309 VLOG_ERR_RL(&rl, "Could not send data on named pipe. Last "
310 "error: %s", ovs_lasterror_to_string());
311 return -EINVAL;
312 }
313 }
314 s->write_pending = false;
315 return retval;
316 }
317
318 result = WriteFile(s->fd, buffer, n, &(DWORD)retval, ov);
319 last_error = GetLastError();
320 if (!result && GetLastError() == ERROR_IO_PENDING) {
321 /* Mark the send operation as pending. */
322 s->write_pending = true;
323 return -EAGAIN;
324 } else if (!result && (last_error == ERROR_PIPE_NOT_CONNECTED
325 || last_error == ERROR_BAD_PIPE
326 || last_error == ERROR_NO_DATA
327 || last_error == ERROR_BROKEN_PIPE)) {
328 /* If the pipe was disconnected, return connection reset. */
329 return -WSAECONNRESET;
330 } else if (!result) {
331 VLOG_ERR_RL(&rl, "Could not send data on synchronous named pipe. Last "
332 "error: %s", ovs_lasterror_to_string());
333 return -EINVAL;
334 }
335 return (retval > 0 ? retval : -EAGAIN);
336 }
337
338 /* Active named pipe wait. */
339 static void
340 windows_wait(struct stream *stream, enum stream_wait_type wait)
341 {
342 struct windows_stream *s = stream_windows_cast(stream);
343 switch (wait) {
344 case STREAM_SEND:
345 poll_wevent_wait(s->write.hEvent);
346 break;
347
348 case STREAM_CONNECT:
349 poll_immediate_wake();
350 break;
351
352 case STREAM_RECV:
353 poll_wevent_wait(s->read.hEvent);
354 break;
355
356 default:
357 OVS_NOT_REACHED();
358 }
359 }
360
361 /* Passive named pipe. */
362 const struct stream_class windows_stream_class = {
363 "unix", /* name */
364 false, /* needs_probes */
365 windows_open, /* open */
366 windows_close, /* close */
367 windows_connect, /* connect */
368 windows_recv, /* recv */
369 windows_send, /* send */
370 NULL, /* run */
371 NULL, /* run_wait */
372 windows_wait, /* wait */
373 };
374
375 struct pwindows_pstream
376 {
377 struct pstream pstream;
378 HANDLE fd;
379 /* Unlink path to be deleted during close. */
380 char *unlink_path;
381 /* Overlapped operation used for connect. */
382 OVERLAPPED connect;
383 /* Flag to check if an operation is pending. */
384 bool pending;
385 /* String used to create the named pipe. */
386 char *pipe_path;
387 };
388
389 const struct pstream_class pwindows_pstream_class;
390
391 static struct pwindows_pstream *
392 pwindows_pstream_cast(struct pstream *pstream)
393 {
394 pstream_assert_class(pstream, &pwindows_pstream_class);
395 return CONTAINER_OF(pstream, struct pwindows_pstream, pstream);
396 }
397
398 /* Create a named pipe with read/write access, overlapped, message mode for
399 * writing, byte mode for reading and with a maximum of 64 active instances. */
400 static HANDLE
401 create_pnpipe(char *name)
402 {
403 SECURITY_ATTRIBUTES sa;
404 sa.nLength = sizeof(sa);
405 sa.lpSecurityDescriptor = NULL;
406 sa.bInheritHandle = TRUE;
407 if (strlen(name) > 256) {
408 VLOG_ERR_RL(&rl, "Named pipe name too long.");
409 return INVALID_HANDLE_VALUE;
410 }
411 return CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
412 PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT,
413 64, BUFSIZE, BUFSIZE, 0, &sa);
414 }
415
416 /* Passive named pipe connect. This function creates a new named pipe and
417 * passes the old handle to the active stream. */
418 static int
419 pwindows_accept(struct pstream *pstream, struct stream **new_streamp)
420 {
421 struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
422 DWORD last_error = 0;
423 DWORD cbRet;
424 HANDLE npipe;
425
426 /* If the connect operation was pending, verify the result. */
427 if (p->pending) {
428 if (!GetOverlappedResult(p->fd, &p->connect, &cbRet, FALSE)) {
429 last_error = GetLastError();
430 if (last_error == ERROR_IO_INCOMPLETE) {
431 /* If the operation is still pending, retry again. */
432 p->pending = true;
433 return EAGAIN;
434 } else {
435 VLOG_ERR_RL(&rl, "Could not connect named pipe. Last "
436 "error: %s", ovs_lasterror_to_string());
437 DisconnectNamedPipe(p->fd);
438 return EINVAL;
439 }
440 }
441 p->pending = false;
442 }
443
444 if (!p->pending && !ConnectNamedPipe(p->fd, &p->connect)) {
445 last_error = GetLastError();
446 if (last_error == ERROR_IO_PENDING) {
447 /* Mark the accept operation as pending. */
448 p->pending = true;
449 return EAGAIN;
450 } else if (last_error != ERROR_PIPE_CONNECTED) {
451 VLOG_ERR_RL(&rl, "Could not connect synchronous named pipe. Last "
452 "error: %s", ovs_lasterror_to_string());
453 DisconnectNamedPipe(p->fd);
454 return EINVAL;
455 } else {
456 /* If the pipe is connected, signal an event. */
457 SetEvent(&p->connect.hEvent);
458 }
459 }
460
461 npipe = create_pnpipe(p->pipe_path);
462 if (npipe == INVALID_HANDLE_VALUE) {
463 VLOG_ERR_RL(&rl, "Could not create a new named pipe after connect. ",
464 ovs_lasterror_to_string());
465 return ENOENT;
466 }
467
468 /* Give the handle p->fd to the new created active stream and specify it
469 * was created by an active stream. */
470 struct windows_stream *p_temp = xmalloc(sizeof *p_temp);
471 stream_init(&p_temp->stream, &windows_stream_class, 0, "unix");
472 p_temp->fd = p->fd;
473 /* Specify it was created by a passive stream. */
474 p_temp->server = true;
475 /* Create events for read/write operations. */
476 memset(&p_temp->read, 0, sizeof(p_temp->read));
477 memset(&p_temp->write, 0, sizeof(p_temp->write));
478 p_temp->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
479 p_temp->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
480 p_temp->read_pending = false;
481 p_temp->write_pending = false;
482 p_temp->retry_connect = false;
483 p_temp->pipe_path = NULL;
484 *new_streamp = &p_temp->stream;
485
486 /* The passive handle p->fd will be the new created handle. */
487 p->fd = npipe;
488 memset(&p->connect, 0, sizeof(p->connect));
489 p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
490 p->pending = false;
491 return 0;
492 }
493
494 /* Passive named pipe close. */
495 static void
496 pwindows_close(struct pstream *pstream)
497 {
498 struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
499 DisconnectNamedPipe(p->fd);
500 CloseHandle(p->fd);
501 CloseHandle(p->connect.hEvent);
502 maybe_unlink_and_free(p->unlink_path);
503 free(p->pipe_path);
504 free(p);
505 }
506
507 /* Passive named pipe wait. */
508 static void
509 pwindows_wait(struct pstream *pstream)
510 {
511 struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
512 poll_wevent_wait(p->connect.hEvent);
513 }
514
515 /* Passive named pipe. */
516 static int
517 pwindows_open(const char *name OVS_UNUSED, char *suffix,
518 struct pstream **pstreamp, uint8_t dscp OVS_UNUSED)
519 {
520 char *bind_path;
521 int error;
522 HANDLE npipe;
523 char *orig_path;
524
525 char *path;
526 if (!strchr(suffix, ':')) {
527 path = xasprintf("%s/%s", ovs_rundir(), suffix);
528 } else {
529 path = xstrdup(suffix);
530 }
531
532 /* Try to create a file under the path location. */
533 FILE *file = fopen(path, "w");
534 if (!file) {
535 free(path);
536 error = errno;
537 VLOG_DBG_RL(&rl, "could not open %s (%s)", path, ovs_strerror(error));
538 return error;
539 } else {
540 fclose(file);
541 }
542
543 orig_path = xstrdup(path);
544 /* Strip slashes from path and create a named pipe using that newly created
545 * string. */
546 bind_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
547 free(path);
548
549 npipe = create_pnpipe(bind_path);
550
551 if (npipe == INVALID_HANDLE_VALUE) {
552 VLOG_ERR_RL(&rl, "Could not create named pipe. Last error: %s",
553 ovs_lasterror_to_string());
554 return ENOENT;
555 }
556
557 struct pwindows_pstream *p = xmalloc(sizeof *p);
558 pstream_init(&p->pstream, &pwindows_pstream_class, name);
559 p->fd = npipe;
560 p->unlink_path = orig_path;
561 memset(&p->connect, 0, sizeof(p->connect));
562 p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
563 p->pending = false;
564 p->pipe_path = bind_path;
565 *pstreamp = &p->pstream;
566 return 0;
567 }
568
569 const struct pstream_class pwindows_pstream_class = {
570 "punix",
571 false, /* probes */
572 pwindows_open, /* open */
573 pwindows_close, /* close */
574 pwindows_accept, /* accept */
575 pwindows_wait, /* wait */
576 };
577
578 /* Helper functions. */
579 static void
580 maybe_unlink_and_free(char *path)
581 {
582 if (path) {
583 fatal_signal_unlink_file_now(path);
584 free(path);
585 }
586 }