zhangmeng
2024-04-22 16935f4aebffdd1b6580b844391a0aa0f4f3012b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//
// Copyright 2017 Garrett D'Amore <garrett@damore.org>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt).  A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
 
#include "core/nng_impl.h"
 
#include <stdio.h>
 
// Windows named pipes won't work for us; we *MUST* use sockets.  This is
// a real sadness, but what can you do.  We use an anonymous socket bound
// to localhost and a connected peer.  This is because folks that want to
// use notification pipes (ugh) are expecting this to work with select(),
// which only supports real winsock sockets.  We use an ephemeral port,
// bound to localhost; some care is taken to prevent other applications on
// the same host from messing us up by accessing the same port.
 
#ifdef NNG_PLATFORM_WINDOWS
 
int
nni_plat_pipe_open(int *wfdp, int *rfdp)
{
    SOCKET rfd = INVALID_SOCKET;
    SOCKET wfd = INVALID_SOCKET;
    SOCKET afd;
 
    struct sockaddr_in addr;
    socklen_t          alen;
    int                one;
    ULONG              yes;
    int                rv;
 
    ZeroMemory(&addr, sizeof(addr));
 
    // Restrict our bind to the loopback address.  We bind to an
    // ephemeral port.
    addr.sin_family      = AF_INET;
    addr.sin_port        = 0;
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
    afd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (afd == INVALID_SOCKET) {
        goto fail;
    }
 
    // Make sure we have exclusive address use...
    one = 1;
    if (setsockopt(afd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) (&one),
            sizeof(one)) != 0) {
        goto fail;
    }
 
    alen = sizeof(addr);
    if (bind(afd, (struct sockaddr *) &addr, alen) != 0) {
        goto fail;
    }
    // What port did we bind to?
    if (getsockname(afd, (struct sockaddr *) &addr, &alen) != 0) {
        goto fail;
    }
 
    // Minimum backlog -- we only expect one connection ever.
    if (listen(afd, 1) != 0) {
        goto fail;
    }
 
    rfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (afd == INVALID_SOCKET) {
        goto fail;
    }
    if (connect(rfd, (struct sockaddr *) &addr, alen) != 0) {
        goto fail;
    }
 
    // Now we have to do the accept dance.  We don't care about the
    // peer address, since know it.
    wfd = accept(afd, NULL, 0);
    if (wfd == INVALID_SOCKET) {
        goto fail;
    }
 
    // Now that we are connected, mark everything non-blocking.
    yes = 1;
    if (ioctlsocket(rfd, FIONBIO, &yes) != 0) {
        goto fail;
    }
    yes = 1;
    if (ioctlsocket(wfd, FIONBIO, &yes) != 0) {
        goto fail;
    }
 
    // Close the listener now that we have the connection.
    closesocket((SOCKET) afd);
    *rfdp = (int) rfd;
    *wfdp = (int) wfd;
    return (0);
 
fail:
    rv = nni_win_error(GetLastError());
    if (afd != INVALID_SOCKET) {
        closesocket(afd);
    }
    if (rfd != INVALID_SOCKET) {
        closesocket(rfd);
    }
    if (wfd != INVALID_SOCKET) {
        closesocket(wfd);
    }
 
    return (rv);
}
 
void
nni_plat_pipe_raise(int wfd)
{
    char c = 1;
 
    send((SOCKET) wfd, &c, 1, 0);
}
 
void
nni_plat_pipe_clear(int rfd)
{
    char buf[32];
 
    for (;;) {
        // Completely drain the pipe, but don't wait.  This coalesces
        // events somewhat.
        if (recv((SOCKET) rfd, buf, sizeof(buf), 0) <= 0) {
            return;
        }
    }
}
 
void
nni_plat_pipe_close(int wfd, int rfd)
{
    closesocket((SOCKET) wfd);
    closesocket((SOCKET) rfd);
}
 
#endif // NNG_PLATFORM_WINDOWS