]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winnps.c
first pass
[PuTTY.git] / windows / winnps.c
1 /*
2  * Windows support module which deals with being a named-pipe server.
3  */
4
5 #include <stdio.h>
6 #include <assert.h>
7
8 #define DEFINE_PLUG_METHOD_MACROS
9 #include "tree234.h"
10 #include "putty.h"
11 #include "network.h"
12 #include "proxy.h"
13 #include "ssh.h"
14
15 #if !defined NO_SECURITY
16
17 #include "winsecur.h"
18
19 Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
20                           Plug plug, int overlapped);
21
22 typedef struct Socket_named_pipe_server_tag *Named_Pipe_Server_Socket;
23 struct Socket_named_pipe_server_tag {
24     const struct socket_function_table *fn;
25     /* the above variable absolutely *must* be the first in this structure */
26
27     /* Parameters for (repeated) creation of named pipe objects */
28     PSECURITY_DESCRIPTOR psd;
29     PACL acl;
30     char *pipename;
31
32     /* The current named pipe object + attempt to connect to it */
33     HANDLE pipehandle;
34     OVERLAPPED connect_ovl;
35     struct handle *callback_handle;    /* winhandl.c's reference */
36
37     /* PuTTY Socket machinery */
38     Plug plug;
39     char *error;
40 };
41
42 static Plug sk_namedpipeserver_plug(Socket s, Plug p)
43 {
44     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
45     Plug ret = ps->plug;
46     if (p)
47         ps->plug = p;
48     return ret;
49 }
50
51 static void sk_namedpipeserver_close(Socket s)
52 {
53     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
54
55     if (ps->callback_handle)
56         handle_free(ps->callback_handle);
57     CloseHandle(ps->pipehandle);
58     CloseHandle(ps->connect_ovl.hEvent);
59     sfree(ps->error);
60     sfree(ps->pipename);
61     if (ps->acl)
62         LocalFree(ps->acl);
63     if (ps->psd)
64         LocalFree(ps->psd);
65     sfree(ps);
66 }
67
68 static const char *sk_namedpipeserver_socket_error(Socket s)
69 {
70     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket) s;
71     return ps->error;
72 }
73
74 static char *sk_namedpipeserver_peer_info(Socket s)
75 {
76     return NULL;
77 }
78
79 static int create_named_pipe(Named_Pipe_Server_Socket ps, int first_instance)
80 {
81     SECURITY_ATTRIBUTES sa;
82
83     memset(&sa, 0, sizeof(sa));
84     sa.nLength = sizeof(sa);
85     sa.lpSecurityDescriptor = ps->psd;
86     sa.bInheritHandle = FALSE;
87
88     ps->pipehandle = CreateNamedPipe
89         (/* lpName */
90          ps->pipename,
91
92          /* dwOpenMode */
93          PIPE_ACCESS_DUPLEX |
94          FILE_FLAG_OVERLAPPED |
95          (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
96
97          /* dwPipeMode */
98          PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT
99 #ifdef PIPE_REJECT_REMOTE_CLIENTS
100          | PIPE_REJECT_REMOTE_CLIENTS
101 #endif
102          ,
103
104          /* nMaxInstances */
105          PIPE_UNLIMITED_INSTANCES,
106
107          /* nOutBufferSize, nInBufferSize */
108          4096, 4096,     /* FIXME: think harder about buffer sizes? */
109
110          /* nDefaultTimeOut */
111          0 /* default timeout */,
112
113          /* lpSecurityAttributes */
114          &sa);
115
116     return ps->pipehandle != INVALID_HANDLE_VALUE;
117 }
118
119 static Socket named_pipe_accept(accept_ctx_t ctx, Plug plug)
120 {
121     HANDLE conn = (HANDLE)ctx.p;
122
123     return make_handle_socket(conn, conn, NULL, plug, TRUE);
124 }
125
126 /*
127  * Dummy SockAddr type which just holds a named pipe address. Only
128  * used for calling plug_log from named_pipe_accept_loop() here.
129  */
130 SockAddr sk_namedpipe_addr(const char *pipename);
131
132 static void named_pipe_accept_loop(Named_Pipe_Server_Socket ps,
133                                    int got_one_already)
134 {
135     while (1) {
136         int error;
137         char *errmsg;
138
139         if (got_one_already) {
140             /* If we were called with a connection already waiting,
141              * skip this step. */
142             got_one_already = FALSE;
143             error = 0;
144         } else {
145             /*
146              * Call ConnectNamedPipe, which might succeed or might
147              * tell us that an overlapped operation is in progress and
148              * we should wait for our event object.
149              */
150             if (ConnectNamedPipe(ps->pipehandle, &ps->connect_ovl))
151                 error = 0;
152             else
153                 error = GetLastError();
154
155             if (error == ERROR_IO_PENDING)
156                 return;
157         }
158
159         if (error == 0 || error == ERROR_PIPE_CONNECTED) {
160             /*
161              * We've successfully retrieved an incoming connection, so
162              * ps->pipehandle now refers to that connection. So
163              * convert that handle into a separate connection-type
164              * Socket, and create a fresh one to be the new listening
165              * pipe.
166              */
167             HANDLE conn = ps->pipehandle;
168             accept_ctx_t actx;
169
170             actx.p = (void *)conn;
171             if (plug_accepting(ps->plug, named_pipe_accept, actx)) {
172                 /*
173                  * If the plug didn't want the connection, might as
174                  * well close this handle.
175                  */
176                 CloseHandle(conn);
177             }
178
179             if (!create_named_pipe(ps, FALSE)) {
180                 error = GetLastError();
181             } else {
182                 /*
183                  * Go round again to see if more connections can be
184                  * got, or to begin waiting on the event object.
185                  */
186                 continue;
187             }
188         }
189
190         errmsg = dupprintf("Error while listening to named pipe: %s",
191                            win_strerror(error));
192         plug_log(ps->plug, 1, sk_namedpipe_addr(ps->pipename), 0,
193                  errmsg, error);
194         sfree(errmsg);
195         break;
196     }
197 }
198
199 static void named_pipe_connect_callback(void *vps)
200 {
201     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket)vps;
202     named_pipe_accept_loop(ps, TRUE);
203 }
204
205 Socket new_named_pipe_listener(const char *pipename, Plug plug)
206 {
207     /*
208      * This socket type is only used for listening, so it should never
209      * be asked to write or flush or set_frozen.
210      */
211     static const struct socket_function_table socket_fn_table = {
212         sk_namedpipeserver_plug,
213         sk_namedpipeserver_close,
214         NULL /* write */,
215         NULL /* write_oob */,
216         NULL /* write_eof */,
217         NULL /* flush */,
218         NULL /* set_frozen */,
219         sk_namedpipeserver_socket_error,
220         sk_namedpipeserver_peer_info,
221     };
222
223     Named_Pipe_Server_Socket ret;
224
225     ret = snew(struct Socket_named_pipe_server_tag);
226     ret->fn = &socket_fn_table;
227     ret->plug = plug;
228     ret->error = NULL;
229     ret->psd = NULL;
230     ret->pipename = dupstr(pipename);
231     ret->acl = NULL;
232     ret->callback_handle = NULL;
233
234     assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0);
235     assert(strchr(pipename + 9, '\\') == NULL);
236
237     if (!make_private_security_descriptor(GENERIC_READ | GENERIC_WRITE,
238                                           &ret->psd, &ret->acl, &ret->error)) {
239         goto cleanup;
240     }
241
242     if (!create_named_pipe(ret, TRUE)) {
243         ret->error = dupprintf("unable to create named pipe '%s': %s",
244                                pipename, win_strerror(GetLastError()));
245         goto cleanup;
246     }
247
248     memset(&ret->connect_ovl, 0, sizeof(ret->connect_ovl));
249     ret->connect_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
250     ret->callback_handle =
251         handle_add_foreign_event(ret->connect_ovl.hEvent,
252                                  named_pipe_connect_callback, ret);
253     named_pipe_accept_loop(ret, FALSE);
254
255   cleanup:
256     return (Socket) ret;
257 }
258
259 #endif /* !defined NO_SECURITY */