source: filezilla/trunk/fuentes/src/putty/windows/winhsock.c @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 7.9 KB
Line 
1/*
2 * General mechanism for wrapping up reading/writing of Windows
3 * HANDLEs into a PuTTY Socket abstraction.
4 */
5
6#include <stdio.h>
7#include <assert.h>
8#include <limits.h>
9
10#define DEFINE_PLUG_METHOD_MACROS
11#include "tree234.h"
12#include "putty.h"
13#include "network.h"
14
15typedef struct Socket_handle_tag *Handle_Socket;
16
17struct Socket_handle_tag {
18    const struct socket_function_table *fn;
19    /* the above variable absolutely *must* be the first in this structure */
20
21    HANDLE send_H, recv_H;
22    struct handle *send_h, *recv_h;
23
24    /*
25     * Freezing one of these sockets is a slightly fiddly business,
26     * because the reads from the handle are happening in a separate
27     * thread as blocking system calls and so once one is in progress
28     * it can't sensibly be interrupted. Hence, after the user tries
29     * to freeze one of these sockets, it's unavoidable that we may
30     * receive one more load of data before we manage to get
31     * winhandl.c to stop reading.
32     */
33    enum {
34        UNFROZEN,  /* reading as normal */
35        FREEZING,  /* have been set to frozen but winhandl is still reading */
36        FROZEN,    /* really frozen - winhandl has been throttled */
37        THAWING    /* we're gradually releasing our remaining data */
38    } frozen;
39    /* We buffer data here if we receive it from winhandl while frozen. */
40    bufchain inputdata;
41
42    char *error;
43
44    Plug plug;
45};
46
47static int handle_gotdata(struct handle *h, void *data, int len)
48{
49    Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
50
51    if (len < 0) {
52        return plug_closing(ps->plug, "Read error from handle",
53                            0, 0);
54    } else if (len == 0) {
55        return plug_closing(ps->plug, NULL, 0, 0);
56    } else {
57        assert(ps->frozen != FREEZING && ps->frozen != THAWING);
58        if (ps->frozen == FREEZING) {
59            /*
60             * If we've received data while this socket is supposed to
61             * be frozen (because the read winhandl.c started before
62             * sk_set_frozen was called has now returned) then buffer
63             * the data for when we unfreeze.
64             */
65            bufchain_add(&ps->inputdata, data, len);
66
67            /*
68             * And return a very large backlog, to prevent further
69             * data arriving from winhandl until we unfreeze.
70             */
71            return INT_MAX;
72        } else {
73            return plug_receive(ps->plug, 0, data, len);
74        }
75    }
76}
77
78static void handle_sentdata(struct handle *h, int new_backlog)
79{
80    Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
81   
82    plug_sent(ps->plug, new_backlog);
83}
84
85static Plug sk_handle_plug(Socket s, Plug p)
86{
87    Handle_Socket ps = (Handle_Socket) s;
88    Plug ret = ps->plug;
89    if (p)
90        ps->plug = p;
91    return ret;
92}
93
94static void sk_handle_close(Socket s)
95{
96    Handle_Socket ps = (Handle_Socket) s;
97
98    handle_free(ps->send_h);
99    handle_free(ps->recv_h);
100    CloseHandle(ps->send_H);
101    if (ps->recv_H != ps->send_H)
102        CloseHandle(ps->recv_H);
103    bufchain_clear(&ps->inputdata);
104
105    sfree(ps);
106}
107
108static int sk_handle_write(Socket s, const char *data, int len)
109{
110    Handle_Socket ps = (Handle_Socket) s;
111
112    return handle_write(ps->send_h, data, len);
113}
114
115static int sk_handle_write_oob(Socket s, const char *data, int len)
116{
117    /*
118     * oob data is treated as inband; nasty, but nothing really
119     * better we can do
120     */
121    return sk_handle_write(s, data, len);
122}
123
124static void sk_handle_write_eof(Socket s)
125{
126    Handle_Socket ps = (Handle_Socket) s;
127
128    handle_write_eof(ps->send_h);
129}
130
131static void sk_handle_flush(Socket s)
132{
133    /* Handle_Socket ps = (Handle_Socket) s; */
134    /* do nothing */
135}
136
137static void handle_socket_unfreeze(void *psv)
138{
139    Handle_Socket ps = (Handle_Socket) psv;
140    void *data;
141    int len, new_backlog;
142
143    /*
144     * If we've been put into a state other than THAWING since the
145     * last callback, then we're done.
146     */
147    if (ps->frozen != THAWING)
148        return;
149
150    /*
151     * Get some of the data we've buffered.
152     */
153    bufchain_prefix(&ps->inputdata, &data, &len);
154    assert(len > 0);
155
156    /*
157     * Hand it off to the plug.
158     */
159    new_backlog = plug_receive(ps->plug, 0, data, len);
160
161    if (bufchain_size(&ps->inputdata) > 0) {
162        /*
163         * If there's still data in our buffer, stay in THAWING state,
164         * and reschedule ourself.
165         */
166        queue_toplevel_callback(handle_socket_unfreeze, ps);
167    } else {
168        /*
169         * Otherwise, we've successfully thawed!
170         */
171        ps->frozen = UNFROZEN;
172        handle_unthrottle(ps->recv_h, new_backlog);
173    }
174}
175
176static void sk_handle_set_frozen(Socket s, int is_frozen)
177{
178    Handle_Socket ps = (Handle_Socket) s;
179
180    if (is_frozen) {
181        switch (ps->frozen) {
182          case FREEZING:
183          case FROZEN:
184            return;                    /* nothing to do */
185
186          case THAWING:
187            /*
188             * We were in the middle of emptying our bufchain, and got
189             * frozen again. In that case, winhandl.c is already
190             * throttled, so just return to FROZEN state. The toplevel
191             * callback will notice and disable itself.
192             */
193            ps->frozen = FROZEN;
194            break;
195
196          case UNFROZEN:
197            /*
198             * The normal case. Go to FREEZING, and expect one more
199             * load of data from winhandl if we're unlucky.
200             */
201            ps->frozen = FREEZING;
202            break;
203        }
204    } else {
205        switch (ps->frozen) {
206          case UNFROZEN:
207          case THAWING:
208            return;                    /* nothing to do */
209
210          case FREEZING:
211            /*
212             * If winhandl didn't send us any data throughout the time
213             * we were frozen, then we'll still be in this state and
214             * can just unfreeze in the trivial way.
215             */
216            assert(bufchain_size(&ps->inputdata) == 0);
217            ps->frozen = UNFROZEN;
218            break;
219
220          case FROZEN:
221            /*
222             * If we have buffered data, go to THAWING and start
223             * releasing it in top-level callbacks.
224             */
225            ps->frozen = THAWING;
226            queue_toplevel_callback(handle_socket_unfreeze, ps);
227        }
228    }
229}
230
231static const char *sk_handle_socket_error(Socket s)
232{
233    Handle_Socket ps = (Handle_Socket) s;
234    return ps->error;
235}
236
237static char *sk_handle_peer_info(Socket s)
238{
239    Handle_Socket ps = (Handle_Socket) s;
240    ULONG pid;
241    static HMODULE kernel32_module;
242    DECL_WINDOWS_FUNCTION(static, BOOL, GetNamedPipeClientProcessId,
243                          (HANDLE, PULONG));
244
245    if (!kernel32_module) {
246        kernel32_module = load_system32_dll("kernel32.dll");
247        GET_WINDOWS_FUNCTION(kernel32_module, GetNamedPipeClientProcessId);
248    }
249
250    /*
251     * Of course, not all handles managed by this module will be
252     * server ends of named pipes, but if they are, then it's useful
253     * to log what we can find out about the client end.
254     */
255    if (p_GetNamedPipeClientProcessId &&
256        p_GetNamedPipeClientProcessId(ps->send_H, &pid))
257        return dupprintf("process id %lu", (unsigned long)pid);
258
259    return NULL;
260}
261
262Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
263                          int overlapped)
264{
265    static const struct socket_function_table socket_fn_table = {
266        sk_handle_plug,
267        sk_handle_close,
268        sk_handle_write,
269        sk_handle_write_oob,
270        sk_handle_write_eof,
271        sk_handle_flush,
272        sk_handle_set_frozen,
273        sk_handle_socket_error,
274        sk_handle_peer_info,
275    };
276
277    Handle_Socket ret;
278    int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0);
279
280    ret = snew(struct Socket_handle_tag);
281    ret->fn = &socket_fn_table;
282    ret->plug = plug;
283    ret->error = NULL;
284    ret->frozen = UNFROZEN;
285    bufchain_init(&ret->inputdata);
286
287    ret->recv_H = recv_H;
288    ret->recv_h = handle_input_new(ret->recv_H, handle_gotdata, ret, flags);
289    ret->send_H = send_H;
290    ret->send_h = handle_output_new(ret->send_H, handle_sentdata, ret, flags);
291
292    return (Socket) ret;
293}
Note: See TracBrowser for help on using the repository browser.