diff mbox

[ovs-dev,v2,1/3] Windows: Local named pipe implementation

Message ID 1468380776-5968-2-git-send-email-aserdean@cloudbasesolutions.com
State Superseded
Delegated to: Guru Shetty
Headers show

Commit Message

Alin Serdean July 13, 2016, 3:32 a.m. UTC
Currently in the case of command line arguments punix/unix, on Windows
we create a file, write a TCP port number to connect. This is a security
concern.

This patch adds support for the command line arguments punix/unix trying
to mimic AF_UNIX behind a local named pipe.

Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
---
v2: Address comments, fix leaks.
---
 lib/stream-windows.c | 587 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 587 insertions(+)
 create mode 100644 lib/stream-windows.c

Comments

Paul Boca July 13, 2016, 8:01 a.m. UTC | #1
Looks good to me.

Acked-by: Paul Boca <pboca@cloudbasesolutions.com>


> -----Original Message-----

> From: dev [mailto:dev-bounces@openvswitch.org] On Behalf Of Alin Serdean

> Sent: Wednesday, July 13, 2016 6:33 AM

> To: dev@openvswitch.org

> Subject: [ovs-dev] [PATCH v2 1/3] Windows: Local named pipe

> implementation

> 

> Currently in the case of command line arguments punix/unix, on Windows

> we create a file, write a TCP port number to connect. This is a security

> concern.

> 

> This patch adds support for the command line arguments punix/unix trying

> to mimic AF_UNIX behind a local named pipe.

> 

> Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>

> ---

> v2: Address comments, fix leaks.

> ---

>  lib/stream-windows.c | 587

> +++++++++++++++++++++++++++++++++++++++++++++++++++

>  1 file changed, 587 insertions(+)

>  create mode 100644 lib/stream-windows.c

> 

> diff --git a/lib/stream-windows.c b/lib/stream-windows.c

> new file mode 100644

> index 0000000..97e6f02

> --- /dev/null

> +++ b/lib/stream-windows.c

> @@ -0,0 +1,587 @@

> +/*

> + * Copyright (c) 2016 Cloudbase Solutions Srl

> + *

> + * Licensed under the Apache License, Version 2.0 (the "License");

> + * you may not use this file except in compliance with the License.

> + * You may obtain a copy of the License at

> + *

> + *     http://www.apache.org/licenses/LICENSE-2.0

> + *

> + * Unless required by applicable law or agreed to in writing, software

> + * distributed under the License is distributed on an "AS IS" BASIS,

> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or

> implied.

> + * See the License for the specific language governing permissions and

> + * limitations under the License.

> + */

> +

> +#include <config.h>

> +#include <errno.h>

> +#include <inttypes.h>

> +#include <netdb.h>

> +#include <poll.h>

> +#include <sys/socket.h>

> +#include <sys/types.h>

> +#include <sys/un.h>

> +#include <stdlib.h>

> +#include <string.h>

> +#include <strsafe.h>

> +#include <unistd.h>

> +#include "packets.h"

> +#include "poll-loop.h"

> +#include "socket-util.h"

> +#include "dirs.h"

> +#include "util.h"

> +#include "stream-provider.h"

> +#include "openvswitch/vlog.h"

> +

> +VLOG_DEFINE_THIS_MODULE(stream_windows);

> +

> +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);

> +

> +static void maybe_unlink_and_free(char *path);

> +

> +/* Buffer size suggested at the creation of the named pipe for reading and

> + * and writing operations */

> +#define BUFSIZE 65000

> +

> +/* Default local prefix of a named pipe */

> +#define LOCAL_PREFIX "\\\\.\\pipe\\"

> +

> +/* This function has the purpose to remove all the slashes received in s */

> +char*

> +remove_slashes(char *s) {

> +    char *p1, *p2;

> +    p1 = p2 = s;

> +

> +    while (*p1) {

> +        if ((*p1) == '\\' || (*p1) == '/') {

> +            p1++;

> +        } else {

> +            *p2 = *p1;

> +            p2++;

> +            p1++;

> +        }

> +    }

> +    *p2 = '\0';

> +    return s;

> +}

> +

> +/* Active named pipe descriptor stream. */

> +struct windows_stream

> +{

> +    struct stream stream;

> +    HANDLE fd;

> +    /* Overlapped operations used for reading/writing */

> +    OVERLAPPED read;

> +    OVERLAPPED write;

> +    /* Flag to check if a reading/writing is pending*/

> +    bool read_pending;

> +    bool write_pending;

> +    /* Flag to check if fd is a server HANDLE. In the case of a server handle

> +     * we have to issue a disconnect before closing the actual handle */

> +    bool server;

> +    bool retry_connect;

> +    char *pipe_path;

> +};

> +

> +static struct windows_stream *

> +stream_windows_cast(struct stream *stream)

> +{

> +    stream_assert_class(stream, &windows_stream_class);

> +    return CONTAINER_OF(stream, struct windows_stream, stream);

> +}

> +

> +HANDLE

> +create_snpipe(char *path) {

> +    return CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,

> +                      OPEN_EXISTING,

> +                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED |

> +                      FILE_FLAG_NO_BUFFERING,

> +                      NULL);

> +}

> +

> +/* Active named pipe open */

> +static int

> +windows_open(const char *name, char *suffix, struct stream **streamp,

> +             uint8_t dscp OVS_UNUSED)

> +{

> +    char *connect_path;

> +    HANDLE npipe;

> +    DWORD mode = PIPE_READMODE_BYTE;

> +    char* path;

> +    FILE* file;

> +    bool retry = false;

> +    /* If the path does not contain a ':', assume it is relative to

> +     * OVS_RUNDIR. */

> +    if (!strchr(suffix, ':')) {

> +        path = xasprintf("%s/%s", ovs_rundir(), suffix);

> +    } else {

> +        path = xstrdup(suffix);

> +    }

> +

> +    /* In the punix/unix argument the assumption is that there is a file

> +     * created in the path (name) */

> +    file = fopen(path, "r");

> +    if (!file) {

> +        free(path);

> +        VLOG_DBG_RL(&rl, "%s: could not open %s (%s)", name, suffix,

> +                    ovs_strerror(errno));

> +        return ENOENT;

> +    } else {

> +        fclose(file);

> +    }

> +

> +    /* Valid pipe names do not have slashes. The assumption is the named

> pipe

> +     * was created on the same path with the slash stripped path and the

> +     * default prefix \\.\pipe\ appended.

> +     * Strip the slashed from the parameter name and append the default

> prefix

> +     */

> +    connect_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));

> +    free(path);

> +

> +    /* Try to connect to the named pipe. In the case all pipe instances are

> +     * busy we set the retry flag to true and we retry  again during the

> +     * connect function. Use overlapped flag and no buffering to ensure

> +     * an asynchronous operations */

> +    npipe = create_snpipe(connect_path);

> +

> +    if (npipe == INVALID_HANDLE_VALUE && GetLastError() ==

> ERROR_PIPE_BUSY) {

> +        retry = true;

> +    }

> +

> +    if (!retry && npipe == INVALID_HANDLE_VALUE) {

> +        VLOG_ERR_RL(&rl, "Could not connect to named pipe: %s",

> +                    ovs_lasterror_to_string());

> +        free(connect_path);

> +        return ENOENT;

> +    }

> +    if (!retry && !SetNamedPipeHandleState(npipe, &mode, NULL, NULL)) {

> +        VLOG_ERR_RL(&rl, "Could not set named pipe options: %s",

> +                    ovs_lasterror_to_string());

> +        free(connect_path);

> +        CloseHandle(npipe);

> +        return ENOENT;

> +    }

> +    struct windows_stream *s = xmalloc(sizeof *s);

> +    stream_init(&s->stream, &windows_stream_class, 0, name);

> +    s->pipe_path = connect_path;

> +    s->fd = npipe;

> +    /* This is a active stream */

> +    s->server = false;

> +    /* Create events for reading and writing to be signaled later */

> +    memset(&s->read, 0, sizeof(s->read));

> +    memset(&s->write, 0, sizeof(s->write));

> +    s->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

> +    s->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

> +    /* Initial read and write operations are not pending */

> +    s->read_pending = false;

> +    s->write_pending = false;

> +    s->retry_connect = retry;

> +    *streamp = &s->stream;

> +    return 0;

> +}

> +

> +/* Active named pipe close */

> +static void

> +windows_close(struct stream *stream)

> +{

> +    struct windows_stream *s = stream_windows_cast(stream);

> +    /* Disconnect the named pipe in case it was a passive stream at first */

> +    if (s->server) {

> +        DisconnectNamedPipe(s->fd);

> +    }

> +    CloseHandle(s->fd);

> +    CloseHandle(s->read.hEvent);

> +    CloseHandle(s->write.hEvent);

> +    if (s->pipe_path) {

> +        free(s->pipe_path);

> +    }

> +    free(s);

> +}

> +

> +/* Active named pipe connect */

> +static int

> +windows_connect(struct stream *stream)

> +{

> +    /* Stub function for connect. We already to the connect in the open

> +     * function */

> +    struct windows_stream *s = stream_windows_cast(stream);

> +

> +    if (!s->retry_connect) {

> +        return 0;

> +    } else {

> +        HANDLE npipe;

> +        npipe = create_snpipe(s->pipe_path);

> +        if (npipe == INVALID_HANDLE_VALUE) {

> +            if (GetLastError() == ERROR_PIPE_BUSY) {

> +                return EAGAIN;

> +            } else {

> +                s->retry_connect = false;

> +                return ENOENT;

> +            }

> +        }

> +        s->retry_connect = false;

> +        s->fd = npipe;

> +        return 0;

> +    }

> +}

> +

> +/* Active named pipe receive */

> +static ssize_t

> +windows_recv(struct stream *stream, void *buffer, size_t n)

> +{

> +    struct windows_stream *s = stream_windows_cast(stream);

> +    ssize_t retval = 0;

> +    boolean result = false;

> +    DWORD last_error = 0;

> +    LPOVERLAPPED  ov = NULL;

> +    ov = &s->read;

> +

> +    /* If the read operation was pending we verify its result */

> +    if (s->read_pending) {

> +        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {

> +            last_error = GetLastError();

> +            if (last_error == ERROR_IO_INCOMPLETE) {

> +                /* If the operation is still pending retry again */

> +                s->read_pending = true;

> +                return -EAGAIN;

> +            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||

> +                       last_error == ERROR_BAD_PIPE ||

> +                       last_error == ERROR_NO_DATA ||

> +                       last_error == ERROR_BROKEN_PIPE) {

> +                /* If the pipe was disconnected return 0 */

> +                return 0;

> +            } else {

> +                VLOG_ERR_RL(&rl, "Could not receive data on named pipe. Last "

> +                            "error: %s", ovs_lasterror_to_string());

> +                return -EINVAL;

> +            }

> +        }

> +        s->read_pending = false;

> +        return retval;

> +    }

> +

> +    result = ReadFile(s->fd, buffer, n, &(DWORD)retval, ov);

> +

> +    if (!result && GetLastError() == ERROR_IO_PENDING) {

> +        /* Pend the read operation */

> +        s->read_pending = true;

> +        return -EAGAIN;

> +    } else if (!result) {

> +        last_error = GetLastError();

> +        if (last_error == ERROR_PIPE_NOT_CONNECTED ||

> +            last_error == ERROR_BAD_PIPE ||

> +            last_error == ERROR_NO_DATA ||

> +            last_error == ERROR_BROKEN_PIPE) {

> +            /* If the pipe was disconnected return 0 */

> +            return 0;

> +        }

> +        VLOG_ERR_RL(&rl, "Could not receive data synchronous on named

> pipe."

> +                    "Last error: %s", ovs_lasterror_to_string());

> +        return -EINVAL;

> +    }

> +

> +    return retval;

> +}

> +

> +/* Active named pipe send */

> +static ssize_t

> +windows_send(struct stream *stream, const void *buffer, size_t n)

> +{

> +    struct windows_stream *s = stream_windows_cast(stream);

> +    ssize_t retval = 0;

> +    boolean result = false;

> +    DWORD last_error = 0;

> +    LPOVERLAPPED  ov = NULL;

> +    ov = &s->write;

> +

> +    /* If the send operation was pending we verify the result */

> +    if (s->write_pending) {

> +        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {

> +            last_error = GetLastError();

> +            if (last_error == ERROR_IO_INCOMPLETE) {

> +                /* If the operation is still pending retry again */

> +                s->write_pending = true;

> +                return -EAGAIN;

> +            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||

> +                       last_error == ERROR_BAD_PIPE ||

> +                       last_error == ERROR_NO_DATA ||

> +                       last_error == ERROR_BROKEN_PIPE) {

> +                /* If the pipe was disconnected return connection reset */

> +                return -WSAECONNRESET;

> +            } else {

> +                VLOG_ERR_RL(&rl, "Could not send data on named pipe. Last "

> +                            "error: %s", ovs_lasterror_to_string());

> +                return -EINVAL;

> +            }

> +        }

> +        s->write_pending = false;

> +        return retval;

> +    }

> +

> +    result = WriteFile(s->fd, buffer, n, &(DWORD)retval, ov);

> +    last_error = GetLastError();

> +    if (!result && GetLastError() == ERROR_IO_PENDING) {

> +        /* Pend the send operation */

> +        s->write_pending = true;

> +        return -EAGAIN;

> +    } else if (!result && (last_error == ERROR_PIPE_NOT_CONNECTED ||

> +                           last_error == ERROR_BAD_PIPE ||

> +                           last_error == ERROR_NO_DATA ||

> +                           last_error == ERROR_BROKEN_PIPE)) {

> +        /* If the pipe was disconnected return connection reset */

> +        return -WSAECONNRESET;

> +    } else if (!result) {

> +        VLOG_ERR_RL(&rl, "Could not send data on synchronous named pipe.

> Last "

> +                    "error: %s", ovs_lasterror_to_string());

> +        return -EINVAL;

> +    }

> +    return (retval > 0 ? retval : -EAGAIN);

> +}

> +

> +/* Active named pipe wait */

> +static void

> +windows_wait(struct stream *stream, enum stream_wait_type wait)

> +{

> +    struct windows_stream *s = stream_windows_cast(stream);

> +    switch (wait) {

> +    case STREAM_SEND:

> +        poll_wevent_wait(s->write.hEvent);

> +        break;

> +

> +    case STREAM_CONNECT:

> +        poll_immediate_wake();

> +        break;

> +

> +    case STREAM_RECV:

> +        poll_wevent_wait(s->read.hEvent);

> +        break;

> +

> +    default:

> +        OVS_NOT_REACHED();

> +    }

> +}

> +

> +/* Passive named pipe descriptor stream. */

> +const struct stream_class windows_stream_class = {

> +    "unix",                     /* name */

> +    false,                      /* needs_probes */

> +    windows_open,               /* open */

> +    windows_close,              /* close */

> +    windows_connect,            /* connect */

> +    windows_recv,               /* recv */

> +    windows_send,               /* send */

> +    NULL,                       /* run */

> +    NULL,                       /* run_wait */

> +    windows_wait,               /* wait */

> +};

> +

> +struct pwindows_pstream

> +{

> +    struct pstream pstream;

> +    HANDLE fd;

> +    /* Unlink path to be deleted during close */

> +    char* unlink_path;

> +    /* Overlapped operation used for connect */

> +    OVERLAPPED connect;

> +    /* Flag to check if an operation is pending */

> +    bool pending;

> +    /* Named pipe path used for creation */

> +    char* pipe_path;

> +};

> +

> +const struct pstream_class pwindows_pstream_class;

> +

> +static struct pwindows_pstream *

> +pwindows_pstream_cast(struct pstream *pstream)

> +{

> +    pstream_assert_class(pstream, &pwindows_pstream_class);

> +    return CONTAINER_OF(pstream, struct pwindows_pstream, pstream);

> +}

> +

> +/* Create a named pipe with read/write access, overlapped, message mode

> for

> + * writing, byte mode for reading with a maximum of 64 active instances */

> +HANDLE

> +create_pnpipe(char *name)

> +{

> +    SECURITY_ATTRIBUTES sa;

> +    sa.nLength = sizeof(sa);

> +    sa.lpSecurityDescriptor = NULL;

> +    sa.bInheritHandle = TRUE;

> +    if (strlen(name) > 256) {

> +        VLOG_ERR_RL(&rl, "Named pipe name too long. Creation terminated");

> +        return INVALID_HANDLE_VALUE;

> +    }

> +    return CreateNamedPipe(name, PIPE_ACCESS_DUPLEX |

> FILE_FLAG_OVERLAPPED,

> +                           PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT,

> +                           64, BUFSIZE, BUFSIZE, 0, &sa);

> +}

> +

> +/* Passive named pipe connect. This function creates a new named pipe

> and

> + * passes the old handle to the active stream */

> +static int

> +pwindows_accept(struct pstream *pstream, struct stream **new_streamp)

> +{

> +    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);

> +    DWORD last_error = 0;

> +    DWORD cbRet;

> +    HANDLE npipe;

> +

> +    /* If the connect operation was pending verify the result */

> +    if (p->pending) {

> +        if (!GetOverlappedResult(p->fd, &p->connect, &cbRet, FALSE)) {

> +            last_error = GetLastError();

> +            if (last_error == ERROR_IO_INCOMPLETE) {

> +                /* If the operation is still pending retry again */

> +                p->pending = true;

> +                return EAGAIN;

> +            } else {

> +                VLOG_ERR_RL(&rl, "Could not connect named pipe. Last "

> +                            "error: %s", ovs_lasterror_to_string());

> +                return EINVAL;

> +            }

> +        }

> +        p->pending = false;

> +    }

> +

> +    if (!p->pending && !ConnectNamedPipe(p->fd, &p->connect)) {

> +        last_error = GetLastError();

> +        if (last_error == ERROR_IO_PENDING) {

> +            /* Pend the connect operation */

> +            p->pending = true;

> +            return EAGAIN;

> +        } else if (last_error != ERROR_PIPE_CONNECTED) {

> +            VLOG_ERR_RL(&rl, "Could not connect synchronous named pipe. Last

> "

> +                        "error: %s", ovs_lasterror_to_string());

> +            return EINVAL;

> +        } else {

> +            /* If the pipe is connected signal an event */

> +            SetEvent(&p->connect.hEvent);

> +        }

> +    }

> +

> +    npipe = create_pnpipe(p->pipe_path);

> +    if (npipe == INVALID_HANDLE_VALUE) {

> +        VLOG_ERR_RL(&rl, "Could not create a new named pipe after connect.

> ",

> +                    ovs_lasterror_to_string());

> +        return ENOENT;

> +    }

> +

> +    /* Give the handle p->fd to the new created active stream and specify it

> +     * was created by an active stream */

> +    struct windows_stream *p_temp = xmalloc(sizeof *p_temp);

> +    stream_init(&p_temp->stream, &windows_stream_class, 0, "unix");

> +    p_temp->fd = p->fd;

> +    /* Specify it was created by a passive stream */

> +    p_temp->server = true;

> +    /* Create events for read/write operations */

> +    memset(&p_temp->read, 0, sizeof(p_temp->read));

> +    memset(&p_temp->write, 0, sizeof(p_temp->write));

> +    p_temp->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

> +    p_temp->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

> +    p_temp->read_pending = false;

> +    p_temp->write_pending = false;

> +    p_temp->retry_connect = false;

> +    p_temp->pipe_path = NULL;

> +    *new_streamp = &p_temp->stream;

> +

> +    /* The passive handle p->fd will be the new created handle */

> +    p->fd = npipe;

> +    memset(&p->connect, 0, sizeof(p->connect));

> +    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

> +    p->pending = false;

> +    return 0;

> +}

> +

> +/* Passive named pipe close */

> +static void

> +pwindows_close(struct pstream *pstream)

> +{

> +    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);

> +    DisconnectNamedPipe(p->fd);

> +    CloseHandle(p->fd);

> +    CloseHandle(p->connect.hEvent);

> +    maybe_unlink_and_free(p->unlink_path);

> +    free(p->pipe_path);

> +    free(p);

> +}

> +

> +/* Passive named pipe wait */

> +void

> +pwindows_wait(struct pstream *pstream)

> +{

> +    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);

> +    poll_wevent_wait(p->connect.hEvent);

> +}

> +

> +/* Passive named pipe */

> +static int

> +pwindows_open(const char *name OVS_UNUSED, char *suffix,

> +              struct pstream **pstreamp, uint8_t dscp OVS_UNUSED)

> +{

> +    char *bind_path;

> +    int error;

> +    HANDLE npipe;

> +    char* orig_path;

> +

> +    char* path;

> +    if (!strchr(suffix, ':')) {

> +        path = xasprintf("%s/%s", ovs_rundir(), suffix);

> +    } else {

> +        path = xstrdup(suffix);

> +    }

> +

> +    /* Try to create a file under the path location */

> +    FILE *file = fopen(path, "w");

> +    if (!file) {

> +        free(path);

> +        error = errno;

> +        VLOG_DBG_RL(&rl, "could not open %s (%s)", path,

> ovs_strerror(error));

> +        return error;

> +    } else {

> +        fclose(file);

> +    }

> +

> +    orig_path = xstrdup(path);

> +    /* Strip slashes from path and create a named pipe using that name */

> +    bind_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));

> +    free(path);

> +

> +    npipe = create_pnpipe(bind_path);

> +

> +    if (npipe == INVALID_HANDLE_VALUE) {

> +        VLOG_ERR_RL(&rl, "Could not create named pipe. Last error: %s",

> +                    ovs_lasterror_to_string());

> +        return ENOENT;

> +    }

> +

> +    struct pwindows_pstream *p = xmalloc(sizeof *p);

> +    pstream_init(&p->pstream, &pwindows_pstream_class, name);

> +    p->fd = npipe;

> +    p->unlink_path = orig_path;

> +    memset(&p->connect, 0, sizeof(p->connect));

> +    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

> +    p->pending = false;

> +    p->pipe_path = bind_path;

> +    *pstreamp = &p->pstream;

> +    return 0;

> +}

> +

> +const struct pstream_class pwindows_pstream_class = {

> +    "punix",

> +    false,                   /* probes */

> +    pwindows_open,           /* open */

> +    pwindows_close,          /* close */

> +    pwindows_accept,         /* accept */

> +    pwindows_wait,           /* wait */

> +};

> +

> +/* Helper functions. */

> +static void

> +maybe_unlink_and_free(char *path)

> +{

> +    if (path) {

> +        fatal_signal_unlink_file_now(path);

> +        free(path);

> +    }

> +}

> --

> 1.9.5.msysgit.0

> _______________________________________________

> dev mailing list

> dev@openvswitch.org

> http://openvswitch.org/mailman/listinfo/dev
Gurucharan Shetty July 14, 2016, 10:26 p.m. UTC | #2
On 12 July 2016 at 20:32, Alin Serdean <aserdean@cloudbasesolutions.com>
wrote:

> Currently in the case of command line arguments punix/unix, on Windows
> we create a file, write a TCP port number to connect. This is a security
> concern.
>
> This patch adds support for the command line arguments punix/unix trying
> to mimic AF_UNIX behind a local named pipe.
>
> Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
>

If I apply this patch, then the compilation fails on Linux. Also, can you
please carefully look at commit cb54a8c57646a1549dcff0c2ad4c2d8b46bc2880
and commit e3f512b07c11de6b297050bb969fd0d8a07f9357 which added these
features in the first place. Those commits add a lot more than what this
series removes.



> ---
> v2: Address comments, fix leaks.
> ---
>  lib/stream-windows.c | 587
> +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 587 insertions(+)
>  create mode 100644 lib/stream-windows.c
>
> diff --git a/lib/stream-windows.c b/lib/stream-windows.c
> new file mode 100644
> index 0000000..97e6f02
> --- /dev/null
> +++ b/lib/stream-windows.c
> @@ -0,0 +1,587 @@
> +/*
> + * Copyright (c) 2016 Cloudbase Solutions Srl
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <netdb.h>
> +#include <poll.h>
> +#include <sys/socket.h>
> +#include <sys/types.h>
> +#include <sys/un.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <strsafe.h>
> +#include <unistd.h>
> +#include "packets.h"
> +#include "poll-loop.h"
> +#include "socket-util.h"
> +#include "dirs.h"
> +#include "util.h"
> +#include "stream-provider.h"
> +#include "openvswitch/vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(stream_windows);
> +
> +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
> +
> +static void maybe_unlink_and_free(char *path);
> +
> +/* Buffer size suggested at the creation of the named pipe for reading and
> + * and writing operations */
> +#define BUFSIZE 65000
> +
> +/* Default local prefix of a named pipe */
> +#define LOCAL_PREFIX "\\\\.\\pipe\\"
> +
> +/* This function has the purpose to remove all the slashes received in s
> */
> +char*
> +remove_slashes(char *s) {
> +    char *p1, *p2;
> +    p1 = p2 = s;
> +
> +    while (*p1) {
> +        if ((*p1) == '\\' || (*p1) == '/') {
> +            p1++;
> +        } else {
> +            *p2 = *p1;
> +            p2++;
> +            p1++;
> +        }
> +    }
> +    *p2 = '\0';
> +    return s;
> +}
> +
> +/* Active named pipe descriptor stream. */
> +struct windows_stream
> +{
> +    struct stream stream;
> +    HANDLE fd;
> +    /* Overlapped operations used for reading/writing */
> +    OVERLAPPED read;
> +    OVERLAPPED write;
> +    /* Flag to check if a reading/writing is pending*/
> +    bool read_pending;
> +    bool write_pending;
> +    /* Flag to check if fd is a server HANDLE. In the case of a server
> handle
> +     * we have to issue a disconnect before closing the actual handle */
> +    bool server;
> +    bool retry_connect;
> +    char *pipe_path;
> +};
> +
> +static struct windows_stream *
> +stream_windows_cast(struct stream *stream)
> +{
> +    stream_assert_class(stream, &windows_stream_class);
> +    return CONTAINER_OF(stream, struct windows_stream, stream);
> +}
> +
> +HANDLE
> +create_snpipe(char *path) {
> +    return CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
> +                      OPEN_EXISTING,
> +                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED |
> +                      FILE_FLAG_NO_BUFFERING,
> +                      NULL);
> +}
> +
> +/* Active named pipe open */
> +static int
> +windows_open(const char *name, char *suffix, struct stream **streamp,
> +             uint8_t dscp OVS_UNUSED)
> +{
> +    char *connect_path;
> +    HANDLE npipe;
> +    DWORD mode = PIPE_READMODE_BYTE;
> +    char* path;
> +    FILE* file;
> +    bool retry = false;
> +    /* If the path does not contain a ':', assume it is relative to
> +     * OVS_RUNDIR. */
> +    if (!strchr(suffix, ':')) {
> +        path = xasprintf("%s/%s", ovs_rundir(), suffix);
> +    } else {
> +        path = xstrdup(suffix);
> +    }
> +
> +    /* In the punix/unix argument the assumption is that there is a file
> +     * created in the path (name) */
> +    file = fopen(path, "r");
> +    if (!file) {
> +        free(path);
> +        VLOG_DBG_RL(&rl, "%s: could not open %s (%s)", name, suffix,
> +                    ovs_strerror(errno));
> +        return ENOENT;
> +    } else {
> +        fclose(file);
> +    }
> +
> +    /* Valid pipe names do not have slashes. The assumption is the named
> pipe
> +     * was created on the same path with the slash stripped path and the
> +     * default prefix \\.\pipe\ appended.
> +     * Strip the slashed from the parameter name and append the default
> prefix
> +     */
> +    connect_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
> +    free(path);
> +
> +    /* Try to connect to the named pipe. In the case all pipe instances
> are
> +     * busy we set the retry flag to true and we retry  again during the
> +     * connect function. Use overlapped flag and no buffering to ensure
> +     * an asynchronous operations */
> +    npipe = create_snpipe(connect_path);
> +
> +    if (npipe == INVALID_HANDLE_VALUE && GetLastError() ==
> ERROR_PIPE_BUSY) {
> +        retry = true;
> +    }
> +
> +    if (!retry && npipe == INVALID_HANDLE_VALUE) {
> +        VLOG_ERR_RL(&rl, "Could not connect to named pipe: %s",
> +                    ovs_lasterror_to_string());
> +        free(connect_path);
> +        return ENOENT;
> +    }
> +    if (!retry && !SetNamedPipeHandleState(npipe, &mode, NULL, NULL)) {
> +        VLOG_ERR_RL(&rl, "Could not set named pipe options: %s",
> +                    ovs_lasterror_to_string());
> +        free(connect_path);
> +        CloseHandle(npipe);
> +        return ENOENT;
> +    }
> +    struct windows_stream *s = xmalloc(sizeof *s);
> +    stream_init(&s->stream, &windows_stream_class, 0, name);
> +    s->pipe_path = connect_path;
> +    s->fd = npipe;
> +    /* This is a active stream */
> +    s->server = false;
> +    /* Create events for reading and writing to be signaled later */
> +    memset(&s->read, 0, sizeof(s->read));
> +    memset(&s->write, 0, sizeof(s->write));
> +    s->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
> +    s->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
> +    /* Initial read and write operations are not pending */
> +    s->read_pending = false;
> +    s->write_pending = false;
> +    s->retry_connect = retry;
> +    *streamp = &s->stream;
> +    return 0;
> +}
> +
> +/* Active named pipe close */
> +static void
> +windows_close(struct stream *stream)
> +{
> +    struct windows_stream *s = stream_windows_cast(stream);
> +    /* Disconnect the named pipe in case it was a passive stream at first
> */
> +    if (s->server) {
> +        DisconnectNamedPipe(s->fd);
> +    }
> +    CloseHandle(s->fd);
> +    CloseHandle(s->read.hEvent);
> +    CloseHandle(s->write.hEvent);
> +    if (s->pipe_path) {
> +        free(s->pipe_path);
> +    }
> +    free(s);
> +}
> +
> +/* Active named pipe connect */
> +static int
> +windows_connect(struct stream *stream)
> +{
> +    /* Stub function for connect. We already to the connect in the open
> +     * function */
> +    struct windows_stream *s = stream_windows_cast(stream);
> +
> +    if (!s->retry_connect) {
> +        return 0;
> +    } else {
> +        HANDLE npipe;
> +        npipe = create_snpipe(s->pipe_path);
> +        if (npipe == INVALID_HANDLE_VALUE) {
> +            if (GetLastError() == ERROR_PIPE_BUSY) {
> +                return EAGAIN;
> +            } else {
> +                s->retry_connect = false;
> +                return ENOENT;
> +            }
> +        }
> +        s->retry_connect = false;
> +        s->fd = npipe;
> +        return 0;
> +    }
> +}
> +
> +/* Active named pipe receive */
> +static ssize_t
> +windows_recv(struct stream *stream, void *buffer, size_t n)
> +{
> +    struct windows_stream *s = stream_windows_cast(stream);
> +    ssize_t retval = 0;
> +    boolean result = false;
> +    DWORD last_error = 0;
> +    LPOVERLAPPED  ov = NULL;
> +    ov = &s->read;
> +
> +    /* If the read operation was pending we verify its result */
> +    if (s->read_pending) {
> +        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
> +            last_error = GetLastError();
> +            if (last_error == ERROR_IO_INCOMPLETE) {
> +                /* If the operation is still pending retry again */
> +                s->read_pending = true;
> +                return -EAGAIN;
> +            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||
> +                       last_error == ERROR_BAD_PIPE ||
> +                       last_error == ERROR_NO_DATA ||
> +                       last_error == ERROR_BROKEN_PIPE) {
> +                /* If the pipe was disconnected return 0 */
> +                return 0;
> +            } else {
> +                VLOG_ERR_RL(&rl, "Could not receive data on named pipe.
> Last "
> +                            "error: %s", ovs_lasterror_to_string());
> +                return -EINVAL;
> +            }
> +        }
> +        s->read_pending = false;
> +        return retval;
> +    }
> +
> +    result = ReadFile(s->fd, buffer, n, &(DWORD)retval, ov);
> +
> +    if (!result && GetLastError() == ERROR_IO_PENDING) {
> +        /* Pend the read operation */
> +        s->read_pending = true;
> +        return -EAGAIN;
> +    } else if (!result) {
> +        last_error = GetLastError();
> +        if (last_error == ERROR_PIPE_NOT_CONNECTED ||
> +            last_error == ERROR_BAD_PIPE ||
> +            last_error == ERROR_NO_DATA ||
> +            last_error == ERROR_BROKEN_PIPE) {
> +            /* If the pipe was disconnected return 0 */
> +            return 0;
> +        }
> +        VLOG_ERR_RL(&rl, "Could not receive data synchronous on named
> pipe."
> +                    "Last error: %s", ovs_lasterror_to_string());
> +        return -EINVAL;
> +    }
> +
> +    return retval;
> +}
> +
> +/* Active named pipe send */
> +static ssize_t
> +windows_send(struct stream *stream, const void *buffer, size_t n)
> +{
> +    struct windows_stream *s = stream_windows_cast(stream);
> +    ssize_t retval = 0;
> +    boolean result = false;
> +    DWORD last_error = 0;
> +    LPOVERLAPPED  ov = NULL;
> +    ov = &s->write;
> +
> +    /* If the send operation was pending we verify the result */
> +    if (s->write_pending) {
> +        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
> +            last_error = GetLastError();
> +            if (last_error == ERROR_IO_INCOMPLETE) {
> +                /* If the operation is still pending retry again */
> +                s->write_pending = true;
> +                return -EAGAIN;
> +            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||
> +                       last_error == ERROR_BAD_PIPE ||
> +                       last_error == ERROR_NO_DATA ||
> +                       last_error == ERROR_BROKEN_PIPE) {
> +                /* If the pipe was disconnected return connection reset */
> +                return -WSAECONNRESET;
> +            } else {
> +                VLOG_ERR_RL(&rl, "Could not send data on named pipe. Last
> "
> +                            "error: %s", ovs_lasterror_to_string());
> +                return -EINVAL;
> +            }
> +        }
> +        s->write_pending = false;
> +        return retval;
> +    }
> +
> +    result = WriteFile(s->fd, buffer, n, &(DWORD)retval, ov);
> +    last_error = GetLastError();
> +    if (!result && GetLastError() == ERROR_IO_PENDING) {
> +        /* Pend the send operation */
> +        s->write_pending = true;
> +        return -EAGAIN;
> +    } else if (!result && (last_error == ERROR_PIPE_NOT_CONNECTED ||
> +                           last_error == ERROR_BAD_PIPE ||
> +                           last_error == ERROR_NO_DATA ||
> +                           last_error == ERROR_BROKEN_PIPE)) {
> +        /* If the pipe was disconnected return connection reset */
> +        return -WSAECONNRESET;
> +    } else if (!result) {
> +        VLOG_ERR_RL(&rl, "Could not send data on synchronous named pipe.
> Last "
> +                    "error: %s", ovs_lasterror_to_string());
> +        return -EINVAL;
> +    }
> +    return (retval > 0 ? retval : -EAGAIN);
> +}
> +
> +/* Active named pipe wait */
> +static void
> +windows_wait(struct stream *stream, enum stream_wait_type wait)
> +{
> +    struct windows_stream *s = stream_windows_cast(stream);
> +    switch (wait) {
> +    case STREAM_SEND:
> +        poll_wevent_wait(s->write.hEvent);
> +        break;
> +
> +    case STREAM_CONNECT:
> +        poll_immediate_wake();
> +        break;
> +
> +    case STREAM_RECV:
> +        poll_wevent_wait(s->read.hEvent);
> +        break;
> +
> +    default:
> +        OVS_NOT_REACHED();
> +    }
> +}
> +
> +/* Passive named pipe descriptor stream. */
> +const struct stream_class windows_stream_class = {
> +    "unix",                     /* name */
> +    false,                      /* needs_probes */
> +    windows_open,               /* open */
> +    windows_close,              /* close */
> +    windows_connect,            /* connect */
> +    windows_recv,               /* recv */
> +    windows_send,               /* send */
> +    NULL,                       /* run */
> +    NULL,                       /* run_wait */
> +    windows_wait,               /* wait */
> +};
> +
> +struct pwindows_pstream
> +{
> +    struct pstream pstream;
> +    HANDLE fd;
> +    /* Unlink path to be deleted during close */
> +    char* unlink_path;
> +    /* Overlapped operation used for connect */
> +    OVERLAPPED connect;
> +    /* Flag to check if an operation is pending */
> +    bool pending;
> +    /* Named pipe path used for creation */
> +    char* pipe_path;
> +};
> +
> +const struct pstream_class pwindows_pstream_class;
> +
> +static struct pwindows_pstream *
> +pwindows_pstream_cast(struct pstream *pstream)
> +{
> +    pstream_assert_class(pstream, &pwindows_pstream_class);
> +    return CONTAINER_OF(pstream, struct pwindows_pstream, pstream);
> +}
> +
> +/* Create a named pipe with read/write access, overlapped, message mode
> for
> + * writing, byte mode for reading with a maximum of 64 active instances */
> +HANDLE
> +create_pnpipe(char *name)
> +{
> +    SECURITY_ATTRIBUTES sa;
> +    sa.nLength = sizeof(sa);
> +    sa.lpSecurityDescriptor = NULL;
> +    sa.bInheritHandle = TRUE;
> +    if (strlen(name) > 256) {
> +        VLOG_ERR_RL(&rl, "Named pipe name too long. Creation terminated");
> +        return INVALID_HANDLE_VALUE;
> +    }
> +    return CreateNamedPipe(name, PIPE_ACCESS_DUPLEX |
> FILE_FLAG_OVERLAPPED,
> +                           PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE |
> PIPE_WAIT,
> +                           64, BUFSIZE, BUFSIZE, 0, &sa);
> +}
> +
> +/* Passive named pipe connect. This function creates a new named pipe and
> + * passes the old handle to the active stream */
> +static int
> +pwindows_accept(struct pstream *pstream, struct stream **new_streamp)
> +{
> +    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
> +    DWORD last_error = 0;
> +    DWORD cbRet;
> +    HANDLE npipe;
> +
> +    /* If the connect operation was pending verify the result */
> +    if (p->pending) {
> +        if (!GetOverlappedResult(p->fd, &p->connect, &cbRet, FALSE)) {
> +            last_error = GetLastError();
> +            if (last_error == ERROR_IO_INCOMPLETE) {
> +                /* If the operation is still pending retry again */
> +                p->pending = true;
> +                return EAGAIN;
> +            } else {
> +                VLOG_ERR_RL(&rl, "Could not connect named pipe. Last "
> +                            "error: %s", ovs_lasterror_to_string());
> +                return EINVAL;
> +            }
> +        }
> +        p->pending = false;
> +    }
> +
> +    if (!p->pending && !ConnectNamedPipe(p->fd, &p->connect)) {
> +        last_error = GetLastError();
> +        if (last_error == ERROR_IO_PENDING) {
> +            /* Pend the connect operation */
> +            p->pending = true;
> +            return EAGAIN;
> +        } else if (last_error != ERROR_PIPE_CONNECTED) {
> +            VLOG_ERR_RL(&rl, "Could not connect synchronous named pipe.
> Last "
> +                        "error: %s", ovs_lasterror_to_string());
> +            return EINVAL;
> +        } else {
> +            /* If the pipe is connected signal an event */
> +            SetEvent(&p->connect.hEvent);
> +        }
> +    }
> +
> +    npipe = create_pnpipe(p->pipe_path);
> +    if (npipe == INVALID_HANDLE_VALUE) {
> +        VLOG_ERR_RL(&rl, "Could not create a new named pipe after
> connect. ",
> +                    ovs_lasterror_to_string());
> +        return ENOENT;
> +    }
> +
> +    /* Give the handle p->fd to the new created active stream and specify
> it
> +     * was created by an active stream */
> +    struct windows_stream *p_temp = xmalloc(sizeof *p_temp);
> +    stream_init(&p_temp->stream, &windows_stream_class, 0, "unix");
> +    p_temp->fd = p->fd;
> +    /* Specify it was created by a passive stream */
> +    p_temp->server = true;
> +    /* Create events for read/write operations */
> +    memset(&p_temp->read, 0, sizeof(p_temp->read));
> +    memset(&p_temp->write, 0, sizeof(p_temp->write));
> +    p_temp->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
> +    p_temp->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
> +    p_temp->read_pending = false;
> +    p_temp->write_pending = false;
> +    p_temp->retry_connect = false;
> +    p_temp->pipe_path = NULL;
> +    *new_streamp = &p_temp->stream;
> +
> +    /* The passive handle p->fd will be the new created handle */
> +    p->fd = npipe;
> +    memset(&p->connect, 0, sizeof(p->connect));
> +    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
> +    p->pending = false;
> +    return 0;
> +}
> +
> +/* Passive named pipe close */
> +static void
> +pwindows_close(struct pstream *pstream)
> +{
> +    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
> +    DisconnectNamedPipe(p->fd);
> +    CloseHandle(p->fd);
> +    CloseHandle(p->connect.hEvent);
> +    maybe_unlink_and_free(p->unlink_path);
> +    free(p->pipe_path);
> +    free(p);
> +}
> +
> +/* Passive named pipe wait */
> +void
> +pwindows_wait(struct pstream *pstream)
> +{
> +    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
> +    poll_wevent_wait(p->connect.hEvent);
> +}
> +
> +/* Passive named pipe */
> +static int
> +pwindows_open(const char *name OVS_UNUSED, char *suffix,
> +              struct pstream **pstreamp, uint8_t dscp OVS_UNUSED)
> +{
> +    char *bind_path;
> +    int error;
> +    HANDLE npipe;
> +    char* orig_path;
> +
> +    char* path;
> +    if (!strchr(suffix, ':')) {
> +        path = xasprintf("%s/%s", ovs_rundir(), suffix);
> +    } else {
> +        path = xstrdup(suffix);
> +    }
> +
> +    /* Try to create a file under the path location */
> +    FILE *file = fopen(path, "w");
> +    if (!file) {
> +        free(path);
> +        error = errno;
> +        VLOG_DBG_RL(&rl, "could not open %s (%s)", path,
> ovs_strerror(error));
> +        return error;
> +    } else {
> +        fclose(file);
> +    }
> +
> +    orig_path = xstrdup(path);
> +    /* Strip slashes from path and create a named pipe using that name */
> +    bind_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
> +    free(path);
> +
> +    npipe = create_pnpipe(bind_path);
> +
> +    if (npipe == INVALID_HANDLE_VALUE) {
> +        VLOG_ERR_RL(&rl, "Could not create named pipe. Last error: %s",
> +                    ovs_lasterror_to_string());
> +        return ENOENT;
> +    }
> +
> +    struct pwindows_pstream *p = xmalloc(sizeof *p);
> +    pstream_init(&p->pstream, &pwindows_pstream_class, name);
> +    p->fd = npipe;
> +    p->unlink_path = orig_path;
> +    memset(&p->connect, 0, sizeof(p->connect));
> +    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
> +    p->pending = false;
> +    p->pipe_path = bind_path;
> +    *pstreamp = &p->pstream;
> +    return 0;
> +}
> +
> +const struct pstream_class pwindows_pstream_class = {
> +    "punix",
> +    false,                   /* probes */
> +    pwindows_open,           /* open */
> +    pwindows_close,          /* close */
> +    pwindows_accept,         /* accept */
> +    pwindows_wait,           /* wait */
> +};
> +
> +/* Helper functions. */
> +static void
> +maybe_unlink_and_free(char *path)
> +{
> +    if (path) {
> +        fatal_signal_unlink_file_now(path);
> +        free(path);
> +    }
> +}
> --
> 1.9.5.msysgit.0
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>
Alin Serdean July 15, 2016, 2 a.m. UTC | #3
Thanks for the review! Comments inlined.

Thanks,
Alin.

De la: Guru Shetty [mailto:guru@ovn.org]
Trimis: Friday, July 15, 2016 1:27 AM
Către: Alin Serdean <aserdean@cloudbasesolutions.com>
Cc: dev@openvswitch.org
Subiect: Re: [ovs-dev] [PATCH v2 1/3] Windows: Local named pipe implementation



On 12 July 2016 at 20:32, Alin Serdean <aserdean@cloudbasesolutions.com<mailto:aserdean@cloudbasesolutions.com>> wrote:
Currently in the case of command line arguments punix/unix, on Windows
we create a file, write a TCP port number to connect. This is a security
concern.

This patch adds support for the command line arguments punix/unix trying
to mimic AF_UNIX behind a local named pipe.

Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com<mailto:aserdean@cloudbasesolutions.com>>


If I apply this patch, then the compilation fails on Linux. Also, can you please carefully look at commit cb54a8c57646a1549dcff0c2ad4c2d8b46bc2880 and commit e3f512b07c11de6b297050bb969fd0d8a07f9357 which added these features in the first place. Those commits add a lot more than what this series removes.

[Alin Gabriel Serdean: ] Sorry about the compilation error I will squash the first two commits. Unless I am missing should I just update the comments and man pages as well, or there is a part of the code I missed?
Gurucharan Shetty July 15, 2016, 2:12 a.m. UTC | #4
On 14 July 2016 at 19:00, Alin Serdean <aserdean@cloudbasesolutions.com>
wrote:

> Thanks for the review! Comments inlined.
>
>
>
> Thanks,
>
> Alin.
>
>
>
> *De la:* Guru Shetty [mailto:guru@ovn.org]
> *Trimis:* Friday, July 15, 2016 1:27 AM
> *Către:* Alin Serdean <aserdean@cloudbasesolutions.com>
> *Cc:* dev@openvswitch.org
> *Subiect:* Re: [ovs-dev] [PATCH v2 1/3] Windows: Local named pipe
> implementation
>
>
>
>
>
>
>
> On 12 July 2016 at 20:32, Alin Serdean <aserdean@cloudbasesolutions.com>
> wrote:
>
> Currently in the case of command line arguments punix/unix, on Windows
> we create a file, write a TCP port number to connect. This is a security
> concern.
>
> This patch adds support for the command line arguments punix/unix trying
> to mimic AF_UNIX behind a local named pipe.
>
> Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
>
>
>
> If I apply this patch, then the compilation fails on Linux. Also, can you
> please carefully look at commit cb54a8c57646a1549dcff0c2ad4c2d8b46bc2880
> and commit e3f512b07c11de6b297050bb969fd0d8a07f9357 which added these
> features in the first place. Those commits add a lot more than what this
> series removes.
>
>
>
> [Alin Gabriel Serdean: ] Sorry about the compilation error I will squash
> the first two commits. Unless I am missing should I just update the
> comments and man pages as well, or there is a part of the code I missed?
>
There were a couple of function declarations and man page changes missed. I
just wanted you take a look at other changes to make sure that they are
alright now too.


>
>
Alin Serdean July 15, 2016, 2:57 a.m. UTC | #5
On 12 July 2016 at 20:32, Alin Serdean <aserdean@cloudbasesolutions.com<mailto:aserdean@cloudbasesolutions.com>> wrote:
Currently in the case of command line arguments punix/unix, on Windows
we create a file, write a TCP port number to connect. This is a security
concern.

This patch adds support for the command line arguments punix/unix trying
to mimic AF_UNIX behind a local named pipe.

Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com<mailto:aserdean@cloudbasesolutions.com>>


If I apply this patch, then the compilation fails on Linux. Also, can you please carefully look at commit cb54a8c57646a1549dcff0c2ad4c2d8b46bc2880 and commit e3f512b07c11de6b297050bb969fd0d8a07f9357 which added these features in the first place. Those commits add a lot more than what this series removes.

[Alin Gabriel Serdean: ] Sorry about the compilation error I will squash the first two commits. Unless I am missing should I just update the comments and man pages as well, or there is a part of the code I missed?
There were a couple of function declarations and man page changes missed. I just wanted you take a look at other changes to make sure that they are alright now too.

[Alin Gabriel Serdean: ] I think I’m blind! I still can’t see the function declarations I missed. Can you please point them out?
Gurucharan Shetty July 15, 2016, 3:42 a.m. UTC | #6
On 14 July 2016 at 19:57, Alin Serdean <aserdean@cloudbasesolutions.com>
wrote:

> On 12 July 2016 at 20:32, Alin Serdean <aserdean@cloudbasesolutions.com>
> wrote:
>
> Currently in the case of command line arguments punix/unix, on Windows
> we create a file, write a TCP port number to connect. This is a security
> concern.
>
> This patch adds support for the command line arguments punix/unix trying
> to mimic AF_UNIX behind a local named pipe.
>
> Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
>
>
>
> If I apply this patch, then the compilation fails on Linux. Also, can you
> please carefully look at commit cb54a8c57646a1549dcff0c2ad4c2d8b46bc2880
> and commit e3f512b07c11de6b297050bb969fd0d8a07f9357 which added these
> features in the first place. Those commits add a lot more than what this
> series removes.
>
>
>
> [Alin Gabriel Serdean: ] Sorry about the compilation error I will squash
> the first two commits. Unless I am missing should I just update the
> comments and man pages as well, or there is a part of the code I missed?
>
> There were a couple of function declarations and man page changes missed.
> I just wanted you take a look at other changes to make sure that they are
> alright now too.
>
>
>
> [Alin Gabriel Serdean: ] I think I’m blind! I still can’t see the function
> declarations I missed. Can you please point them out?
>
You are fine. I think you missed
https://github.com/openvswitch/ovs/blob/master/lib/stream-provider.h#L195
(not function declarations)


>
>
Gurucharan Shetty July 15, 2016, 3:45 a.m. UTC | #7
On 14 July 2016 at 20:42, Guru Shetty <guru@ovn.org> wrote:

>
>
> On 14 July 2016 at 19:57, Alin Serdean <aserdean@cloudbasesolutions.com>
> wrote:
>
>> On 12 July 2016 at 20:32, Alin Serdean <aserdean@cloudbasesolutions.com>
>> wrote:
>>
>> Currently in the case of command line arguments punix/unix, on Windows
>> we create a file, write a TCP port number to connect. This is a security
>> concern.
>>
>> This patch adds support for the command line arguments punix/unix trying
>> to mimic AF_UNIX behind a local named pipe.
>>
>> Signed-off-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com>
>>
>>
>>
>> If I apply this patch, then the compilation fails on Linux. Also, can you
>> please carefully look at commit cb54a8c57646a1549dcff0c2ad4c2d8b46bc2880
>> and commit e3f512b07c11de6b297050bb969fd0d8a07f9357 which added these
>> features in the first place. Those commits add a lot more than what this
>> series removes.
>>
>>
>>
>> [Alin Gabriel Serdean: ] Sorry about the compilation error I will squash
>> the first two commits. Unless I am missing should I just update the
>> comments and man pages as well, or there is a part of the code I missed?
>>
>> There were a couple of function declarations and man page changes missed.
>> I just wanted you take a look at other changes to make sure that they are
>> alright now too.
>>
>>
>>
>> [Alin Gabriel Serdean: ] I think I’m blind! I still can’t see the
>> function declarations I missed. Can you please point them out?
>>
> You are fine. I think you missed
> https://github.com/openvswitch/ovs/blob/master/lib/stream-provider.h#L195
> (not function declarations)
>
Never mind. I think you still need them.

>
>
>>
>>
>
>
diff mbox

Patch

diff --git a/lib/stream-windows.c b/lib/stream-windows.c
new file mode 100644
index 0000000..97e6f02
--- /dev/null
+++ b/lib/stream-windows.c
@@ -0,0 +1,587 @@ 
+/*
+ * Copyright (c) 2016 Cloudbase Solutions Srl
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strsafe.h>
+#include <unistd.h>
+#include "packets.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "dirs.h"
+#include "util.h"
+#include "stream-provider.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(stream_windows);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
+
+static void maybe_unlink_and_free(char *path);
+
+/* Buffer size suggested at the creation of the named pipe for reading and
+ * and writing operations */
+#define BUFSIZE 65000
+
+/* Default local prefix of a named pipe */
+#define LOCAL_PREFIX "\\\\.\\pipe\\"
+
+/* This function has the purpose to remove all the slashes received in s */
+char*
+remove_slashes(char *s) {
+    char *p1, *p2;
+    p1 = p2 = s;
+
+    while (*p1) {
+        if ((*p1) == '\\' || (*p1) == '/') {
+            p1++;
+        } else {
+            *p2 = *p1;
+            p2++;
+            p1++;
+        }
+    }
+    *p2 = '\0';
+    return s;
+}
+
+/* Active named pipe descriptor stream. */
+struct windows_stream
+{
+    struct stream stream;
+    HANDLE fd;
+    /* Overlapped operations used for reading/writing */
+    OVERLAPPED read;
+    OVERLAPPED write;
+    /* Flag to check if a reading/writing is pending*/
+    bool read_pending;
+    bool write_pending;
+    /* Flag to check if fd is a server HANDLE. In the case of a server handle
+     * we have to issue a disconnect before closing the actual handle */
+    bool server;
+    bool retry_connect;
+    char *pipe_path;
+};
+
+static struct windows_stream *
+stream_windows_cast(struct stream *stream)
+{
+    stream_assert_class(stream, &windows_stream_class);
+    return CONTAINER_OF(stream, struct windows_stream, stream);
+}
+
+HANDLE
+create_snpipe(char *path) {
+    return CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                      OPEN_EXISTING,
+                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED |
+                      FILE_FLAG_NO_BUFFERING,
+                      NULL);
+}
+
+/* Active named pipe open */
+static int
+windows_open(const char *name, char *suffix, struct stream **streamp,
+             uint8_t dscp OVS_UNUSED)
+{
+    char *connect_path;
+    HANDLE npipe;
+    DWORD mode = PIPE_READMODE_BYTE;
+    char* path;
+    FILE* file;
+    bool retry = false;
+    /* If the path does not contain a ':', assume it is relative to
+     * OVS_RUNDIR. */
+    if (!strchr(suffix, ':')) {
+        path = xasprintf("%s/%s", ovs_rundir(), suffix);
+    } else {
+        path = xstrdup(suffix);
+    }
+
+    /* In the punix/unix argument the assumption is that there is a file
+     * created in the path (name) */
+    file = fopen(path, "r");
+    if (!file) {
+        free(path);
+        VLOG_DBG_RL(&rl, "%s: could not open %s (%s)", name, suffix,
+                    ovs_strerror(errno));
+        return ENOENT;
+    } else {
+        fclose(file);
+    }
+
+    /* Valid pipe names do not have slashes. The assumption is the named pipe
+     * was created on the same path with the slash stripped path and the
+     * default prefix \\.\pipe\ appended.
+     * Strip the slashed from the parameter name and append the default prefix
+     */
+    connect_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
+    free(path);
+
+    /* Try to connect to the named pipe. In the case all pipe instances are
+     * busy we set the retry flag to true and we retry  again during the
+     * connect function. Use overlapped flag and no buffering to ensure 
+     * an asynchronous operations */
+    npipe = create_snpipe(connect_path);
+
+    if (npipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY) {
+        retry = true;
+    }
+
+    if (!retry && npipe == INVALID_HANDLE_VALUE) {
+        VLOG_ERR_RL(&rl, "Could not connect to named pipe: %s",
+                    ovs_lasterror_to_string());
+        free(connect_path);
+        return ENOENT;
+    }
+    if (!retry && !SetNamedPipeHandleState(npipe, &mode, NULL, NULL)) {
+        VLOG_ERR_RL(&rl, "Could not set named pipe options: %s",
+                    ovs_lasterror_to_string());
+        free(connect_path);
+        CloseHandle(npipe);
+        return ENOENT;
+    }
+    struct windows_stream *s = xmalloc(sizeof *s);
+    stream_init(&s->stream, &windows_stream_class, 0, name);
+    s->pipe_path = connect_path;
+    s->fd = npipe;
+    /* This is a active stream */
+    s->server = false;
+    /* Create events for reading and writing to be signaled later */
+    memset(&s->read, 0, sizeof(s->read));
+    memset(&s->write, 0, sizeof(s->write));
+    s->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    s->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    /* Initial read and write operations are not pending */
+    s->read_pending = false;
+    s->write_pending = false;
+    s->retry_connect = retry;
+    *streamp = &s->stream;
+    return 0;
+}
+
+/* Active named pipe close */
+static void
+windows_close(struct stream *stream)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    /* Disconnect the named pipe in case it was a passive stream at first */
+    if (s->server) {
+        DisconnectNamedPipe(s->fd);
+    }
+    CloseHandle(s->fd);
+    CloseHandle(s->read.hEvent);
+    CloseHandle(s->write.hEvent);
+    if (s->pipe_path) {
+        free(s->pipe_path);
+    }
+    free(s);
+}
+
+/* Active named pipe connect */
+static int
+windows_connect(struct stream *stream)
+{
+    /* Stub function for connect. We already to the connect in the open
+     * function */
+    struct windows_stream *s = stream_windows_cast(stream);
+
+    if (!s->retry_connect) {
+        return 0;
+    } else {
+        HANDLE npipe;
+        npipe = create_snpipe(s->pipe_path);
+        if (npipe == INVALID_HANDLE_VALUE) {
+            if (GetLastError() == ERROR_PIPE_BUSY) {
+                return EAGAIN;
+            } else {
+                s->retry_connect = false;
+                return ENOENT;
+            }
+        }
+        s->retry_connect = false;
+        s->fd = npipe;
+        return 0;
+    }
+}
+
+/* Active named pipe receive */
+static ssize_t
+windows_recv(struct stream *stream, void *buffer, size_t n)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    ssize_t retval = 0;
+    boolean result = false;
+    DWORD last_error = 0;
+    LPOVERLAPPED  ov = NULL;
+    ov = &s->read;
+
+    /* If the read operation was pending we verify its result */
+    if (s->read_pending) {
+        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
+            last_error = GetLastError();
+            if (last_error == ERROR_IO_INCOMPLETE) {
+                /* If the operation is still pending retry again */
+                s->read_pending = true;
+                return -EAGAIN;
+            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||
+                       last_error == ERROR_BAD_PIPE ||
+                       last_error == ERROR_NO_DATA ||
+                       last_error == ERROR_BROKEN_PIPE) {
+                /* If the pipe was disconnected return 0 */
+                return 0;
+            } else {
+                VLOG_ERR_RL(&rl, "Could not receive data on named pipe. Last "
+                            "error: %s", ovs_lasterror_to_string());
+                return -EINVAL;
+            }
+        }
+        s->read_pending = false;
+        return retval;
+    }
+
+    result = ReadFile(s->fd, buffer, n, &(DWORD)retval, ov);
+
+    if (!result && GetLastError() == ERROR_IO_PENDING) {
+        /* Pend the read operation */
+        s->read_pending = true;
+        return -EAGAIN;
+    } else if (!result) {
+        last_error = GetLastError();
+        if (last_error == ERROR_PIPE_NOT_CONNECTED ||
+            last_error == ERROR_BAD_PIPE ||
+            last_error == ERROR_NO_DATA ||
+            last_error == ERROR_BROKEN_PIPE) {
+            /* If the pipe was disconnected return 0 */
+            return 0;
+        }
+        VLOG_ERR_RL(&rl, "Could not receive data synchronous on named pipe."
+                    "Last error: %s", ovs_lasterror_to_string());
+        return -EINVAL;
+    }
+
+    return retval;
+}
+
+/* Active named pipe send */
+static ssize_t
+windows_send(struct stream *stream, const void *buffer, size_t n)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    ssize_t retval = 0;
+    boolean result = false;
+    DWORD last_error = 0;
+    LPOVERLAPPED  ov = NULL;
+    ov = &s->write;
+
+    /* If the send operation was pending we verify the result */
+    if (s->write_pending) {
+        if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) {
+            last_error = GetLastError();
+            if (last_error == ERROR_IO_INCOMPLETE) {
+                /* If the operation is still pending retry again */
+                s->write_pending = true;
+                return -EAGAIN;
+            } else if (last_error == ERROR_PIPE_NOT_CONNECTED ||
+                       last_error == ERROR_BAD_PIPE ||
+                       last_error == ERROR_NO_DATA ||
+                       last_error == ERROR_BROKEN_PIPE) {
+                /* If the pipe was disconnected return connection reset */
+                return -WSAECONNRESET;
+            } else {
+                VLOG_ERR_RL(&rl, "Could not send data on named pipe. Last "
+                            "error: %s", ovs_lasterror_to_string());
+                return -EINVAL;
+            }
+        }
+        s->write_pending = false;
+        return retval;
+    }
+
+    result = WriteFile(s->fd, buffer, n, &(DWORD)retval, ov);
+    last_error = GetLastError();
+    if (!result && GetLastError() == ERROR_IO_PENDING) {
+        /* Pend the send operation */
+        s->write_pending = true;
+        return -EAGAIN;
+    } else if (!result && (last_error == ERROR_PIPE_NOT_CONNECTED ||
+                           last_error == ERROR_BAD_PIPE ||
+                           last_error == ERROR_NO_DATA ||
+                           last_error == ERROR_BROKEN_PIPE)) {
+        /* If the pipe was disconnected return connection reset */
+        return -WSAECONNRESET;
+    } else if (!result) {
+        VLOG_ERR_RL(&rl, "Could not send data on synchronous named pipe. Last "
+                    "error: %s", ovs_lasterror_to_string());
+        return -EINVAL;
+    }
+    return (retval > 0 ? retval : -EAGAIN);
+}
+
+/* Active named pipe wait */
+static void
+windows_wait(struct stream *stream, enum stream_wait_type wait)
+{
+    struct windows_stream *s = stream_windows_cast(stream);
+    switch (wait) {
+    case STREAM_SEND:
+        poll_wevent_wait(s->write.hEvent);
+        break;
+
+    case STREAM_CONNECT:
+        poll_immediate_wake();
+        break;
+
+    case STREAM_RECV:
+        poll_wevent_wait(s->read.hEvent);
+        break;
+
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
+/* Passive named pipe descriptor stream. */
+const struct stream_class windows_stream_class = {
+    "unix",                     /* name */
+    false,                      /* needs_probes */
+    windows_open,               /* open */
+    windows_close,              /* close */
+    windows_connect,            /* connect */
+    windows_recv,               /* recv */
+    windows_send,               /* send */
+    NULL,                       /* run */
+    NULL,                       /* run_wait */
+    windows_wait,               /* wait */
+};
+
+struct pwindows_pstream
+{
+    struct pstream pstream;
+    HANDLE fd;
+    /* Unlink path to be deleted during close */
+    char* unlink_path;
+    /* Overlapped operation used for connect */
+    OVERLAPPED connect;
+    /* Flag to check if an operation is pending */
+    bool pending;
+    /* Named pipe path used for creation */
+    char* pipe_path;
+};
+
+const struct pstream_class pwindows_pstream_class;
+
+static struct pwindows_pstream *
+pwindows_pstream_cast(struct pstream *pstream)
+{
+    pstream_assert_class(pstream, &pwindows_pstream_class);
+    return CONTAINER_OF(pstream, struct pwindows_pstream, pstream);
+}
+
+/* Create a named pipe with read/write access, overlapped, message mode for
+ * writing, byte mode for reading with a maximum of 64 active instances */
+HANDLE
+create_pnpipe(char *name)
+{
+    SECURITY_ATTRIBUTES sa;
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;
+    if (strlen(name) > 256) {
+        VLOG_ERR_RL(&rl, "Named pipe name too long. Creation terminated");
+        return INVALID_HANDLE_VALUE;
+    }
+    return CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                           PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | PIPE_WAIT,
+                           64, BUFSIZE, BUFSIZE, 0, &sa);
+}
+
+/* Passive named pipe connect. This function creates a new named pipe and
+ * passes the old handle to the active stream */
+static int
+pwindows_accept(struct pstream *pstream, struct stream **new_streamp)
+{
+    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+    DWORD last_error = 0;
+    DWORD cbRet;
+    HANDLE npipe;
+
+    /* If the connect operation was pending verify the result */
+    if (p->pending) {
+        if (!GetOverlappedResult(p->fd, &p->connect, &cbRet, FALSE)) {
+            last_error = GetLastError();
+            if (last_error == ERROR_IO_INCOMPLETE) {
+                /* If the operation is still pending retry again */
+                p->pending = true;
+                return EAGAIN;
+            } else {
+                VLOG_ERR_RL(&rl, "Could not connect named pipe. Last "
+                            "error: %s", ovs_lasterror_to_string());
+                return EINVAL;
+            }
+        }
+        p->pending = false;
+    }
+
+    if (!p->pending && !ConnectNamedPipe(p->fd, &p->connect)) {
+        last_error = GetLastError();
+        if (last_error == ERROR_IO_PENDING) {
+            /* Pend the connect operation */
+            p->pending = true;
+            return EAGAIN;
+        } else if (last_error != ERROR_PIPE_CONNECTED) {
+            VLOG_ERR_RL(&rl, "Could not connect synchronous named pipe. Last "
+                        "error: %s", ovs_lasterror_to_string());
+            return EINVAL;
+        } else {
+            /* If the pipe is connected signal an event */
+            SetEvent(&p->connect.hEvent);
+        }
+    }
+
+    npipe = create_pnpipe(p->pipe_path);
+    if (npipe == INVALID_HANDLE_VALUE) {
+        VLOG_ERR_RL(&rl, "Could not create a new named pipe after connect. ",
+                    ovs_lasterror_to_string());
+        return ENOENT;
+    }
+
+    /* Give the handle p->fd to the new created active stream and specify it
+     * was created by an active stream */
+    struct windows_stream *p_temp = xmalloc(sizeof *p_temp);
+    stream_init(&p_temp->stream, &windows_stream_class, 0, "unix");
+    p_temp->fd = p->fd;
+    /* Specify it was created by a passive stream */
+    p_temp->server = true;
+    /* Create events for read/write operations */
+    memset(&p_temp->read, 0, sizeof(p_temp->read));
+    memset(&p_temp->write, 0, sizeof(p_temp->write));
+    p_temp->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p_temp->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p_temp->read_pending = false;
+    p_temp->write_pending = false;
+    p_temp->retry_connect = false;
+    p_temp->pipe_path = NULL;
+    *new_streamp = &p_temp->stream;
+
+    /* The passive handle p->fd will be the new created handle */
+    p->fd = npipe;
+    memset(&p->connect, 0, sizeof(p->connect));
+    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p->pending = false;
+    return 0;
+}
+
+/* Passive named pipe close */
+static void
+pwindows_close(struct pstream *pstream)
+{
+    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+    DisconnectNamedPipe(p->fd);
+    CloseHandle(p->fd);
+    CloseHandle(p->connect.hEvent);
+    maybe_unlink_and_free(p->unlink_path);
+    free(p->pipe_path);
+    free(p);
+}
+
+/* Passive named pipe wait */
+void
+pwindows_wait(struct pstream *pstream)
+{
+    struct pwindows_pstream *p = pwindows_pstream_cast(pstream);
+    poll_wevent_wait(p->connect.hEvent);
+}
+
+/* Passive named pipe */
+static int
+pwindows_open(const char *name OVS_UNUSED, char *suffix,
+              struct pstream **pstreamp, uint8_t dscp OVS_UNUSED)
+{
+    char *bind_path;
+    int error;
+    HANDLE npipe;
+    char* orig_path;
+
+    char* path;
+    if (!strchr(suffix, ':')) {
+        path = xasprintf("%s/%s", ovs_rundir(), suffix);
+    } else {
+        path = xstrdup(suffix);
+    }
+
+    /* Try to create a file under the path location */
+    FILE *file = fopen(path, "w");
+    if (!file) {
+        free(path);
+        error = errno;
+        VLOG_DBG_RL(&rl, "could not open %s (%s)", path, ovs_strerror(error));
+        return error;
+    } else {
+        fclose(file);
+    }
+
+    orig_path = xstrdup(path);
+    /* Strip slashes from path and create a named pipe using that name */
+    bind_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path));
+    free(path);
+
+    npipe = create_pnpipe(bind_path);
+
+    if (npipe == INVALID_HANDLE_VALUE) {
+        VLOG_ERR_RL(&rl, "Could not create named pipe. Last error: %s",
+                    ovs_lasterror_to_string());
+        return ENOENT;
+    }
+
+    struct pwindows_pstream *p = xmalloc(sizeof *p);
+    pstream_init(&p->pstream, &pwindows_pstream_class, name);
+    p->fd = npipe;
+    p->unlink_path = orig_path;
+    memset(&p->connect, 0, sizeof(p->connect));
+    p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+    p->pending = false;
+    p->pipe_path = bind_path;
+    *pstreamp = &p->pstream;
+    return 0;
+}
+
+const struct pstream_class pwindows_pstream_class = {
+    "punix",
+    false,                   /* probes */
+    pwindows_open,           /* open */
+    pwindows_close,          /* close */
+    pwindows_accept,         /* accept */
+    pwindows_wait,           /* wait */
+};
+
+/* Helper functions. */
+static void
+maybe_unlink_and_free(char *path)
+{
+    if (path) {
+        fatal_signal_unlink_file_now(path);
+        free(path);
+    }
+}