]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - windows/winnps.c
9cc8176fdefdc63091add8123af4886d3836127e
[PuTTY_svn.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, Plug plug,
20                           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     PSID networksid;
30     PACL acl;
31     char *pipename;
32
33     /* The current named pipe object + attempt to connect to it */
34     HANDLE pipehandle;
35     OVERLAPPED connect_ovl;
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     CloseHandle(ps->pipehandle);
56     CloseHandle(ps->connect_ovl.hEvent);
57     sfree(ps->error);
58     sfree(ps->pipename);
59     if (ps->networksid)
60         LocalFree(ps->networksid);
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 int create_named_pipe(Named_Pipe_Server_Socket ps, int first_instance)
75 {
76     SECURITY_ATTRIBUTES sa;
77
78     memset(&sa, 0, sizeof(sa));
79     sa.nLength = sizeof(sa);
80     sa.lpSecurityDescriptor = ps->psd;
81     sa.bInheritHandle = FALSE;
82
83     ps->pipehandle = CreateNamedPipe
84         (/* lpName */
85          ps->pipename,
86
87          /* dwOpenMode */
88          PIPE_ACCESS_DUPLEX |
89          FILE_FLAG_OVERLAPPED |
90          (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
91
92          /* dwPipeMode */
93          PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT
94 #ifdef PIPE_REJECT_REMOTE_CLIENTS
95          | PIPE_REJECT_REMOTE_CLIENTS
96 #endif
97          ,
98
99          /* nMaxInstances */
100          PIPE_UNLIMITED_INSTANCES,
101
102          /* nOutBufferSize, nInBufferSize */
103          4096, 4096,     /* FIXME: think harder about buffer sizes? */
104
105          /* nDefaultTimeOut */
106          0 /* default timeout */,
107
108          /* lpSecurityAttributes */
109          &sa);
110
111     return ps->pipehandle != INVALID_HANDLE_VALUE;
112 }
113
114 static Socket named_pipe_accept(accept_ctx_t ctx, Plug plug)
115 {
116     HANDLE conn = (HANDLE)ctx.p;
117
118     return make_handle_socket(conn, conn, plug, TRUE);
119 }
120
121 /*
122  * Dummy SockAddr type which just holds a named pipe address. Only
123  * used for calling plug_log from named_pipe_accept_loop() here.
124  */
125 SockAddr sk_namedpipe_addr(const char *pipename);
126
127 static void named_pipe_accept_loop(Named_Pipe_Server_Socket ps,
128                                    int got_one_already)
129 {
130     while (1) {
131         int error;
132         char *errmsg;
133
134         if (got_one_already) {
135             /* If we were called with a connection already waiting,
136              * skip this step. */
137             got_one_already = FALSE;
138             error = 0;
139         } else {
140             /*
141              * Call ConnectNamedPipe, which might succeed or might
142              * tell us that an overlapped operation is in progress and
143              * we should wait for our event object.
144              */
145             if (ConnectNamedPipe(ps->pipehandle, &ps->connect_ovl))
146                 error = 0;
147             else
148                 error = GetLastError();
149
150             if (error == ERROR_IO_PENDING)
151                 return;
152         }
153
154         if (error == 0 || error == ERROR_PIPE_CONNECTED) {
155             /*
156              * We've successfully retrieved an incoming connection, so
157              * ps->pipehandle now refers to that connection. So
158              * convert that handle into a separate connection-type
159              * Socket, and create a fresh one to be the new listening
160              * pipe.
161              */
162             HANDLE conn = ps->pipehandle;
163             accept_ctx_t actx;
164
165             actx.p = (void *)conn;
166             if (plug_accepting(ps->plug, named_pipe_accept, actx)) {
167                 /*
168                  * If the plug didn't want the connection, might as
169                  * well close this handle.
170                  */
171                 CloseHandle(conn);
172             }
173
174             if (!create_named_pipe(ps, FALSE)) {
175                 error = GetLastError();
176             } else {
177                 /*
178                  * Go round again to see if more connections can be
179                  * got, or to begin waiting on the event object.
180                  */
181                 continue;
182             }
183         }
184
185         errmsg = dupprintf("Error while listening to named pipe: %s",
186                            win_strerror(error));
187         plug_log(ps->plug, 1, sk_namedpipe_addr(ps->pipename), 0,
188                  errmsg, error);
189         sfree(errmsg);
190         break;
191     }
192 }
193
194 static void named_pipe_connect_callback(void *vps)
195 {
196     Named_Pipe_Server_Socket ps = (Named_Pipe_Server_Socket)vps;
197     named_pipe_accept_loop(ps, TRUE);
198 }
199
200 Socket new_named_pipe_listener(const char *pipename, Plug plug)
201 {
202     /*
203      * This socket type is only used for listening, so it should never
204      * be asked to write or flush or set_frozen.
205      */
206     static const struct socket_function_table socket_fn_table = {
207         sk_namedpipeserver_plug,
208         sk_namedpipeserver_close,
209         NULL /* write */,
210         NULL /* write_oob */,
211         NULL /* write_eof */,
212         NULL /* flush */,
213         NULL /* set_frozen */,
214         sk_namedpipeserver_socket_error
215     };
216
217     Named_Pipe_Server_Socket ret;
218
219     ret = snew(struct Socket_named_pipe_server_tag);
220     ret->fn = &socket_fn_table;
221     ret->plug = plug;
222     ret->error = NULL;
223     ret->psd = NULL;
224     ret->pipename = dupstr(pipename);
225     ret->networksid = NULL;
226     ret->acl = NULL;
227
228     assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0);
229     assert(strchr(pipename + 9, '\\') == NULL);
230
231     if (!make_private_security_descriptor(GENERIC_READ | GENERIC_WRITE,
232                                           &ret->psd, &ret->networksid,
233                                           &ret->acl, &ret->error)) {
234         goto cleanup;
235     }
236
237     if (!create_named_pipe(ret, TRUE)) {
238         ret->error = dupprintf("unable to create named pipe '%s': %s",
239                                pipename, win_strerror(GetLastError()));
240         goto cleanup;
241     }
242
243     memset(&ret->connect_ovl, 0, sizeof(ret->connect_ovl));
244     ret->connect_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
245     handle_add_foreign_event(ret->connect_ovl.hEvent,
246                              named_pipe_connect_callback, ret);
247     named_pipe_accept_loop(ret, FALSE);
248
249   cleanup:
250     return (Socket) ret;
251 }
252
253 #endif /* !defined NO_SECURITY */