]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - winnet.c
Make memory management uniform: _everything_ now goes through the
[PuTTY.git] / winnet.c
1 /*
2  * Windows networking abstraction.
3  */
4
5 #include <windows.h>
6 #include <winsock.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9
10 #include "putty.h"
11 #include "network.h"
12 #include "tree234.h"
13
14 #define BUFFER_GRANULE 512
15
16 struct Socket_tag {
17     char *error;
18     SOCKET s;
19     sk_receiver_t receiver;
20     void *private_ptr;
21     struct buffer *head, *tail;
22     int writable;
23     int in_oob, sending_oob;
24 };
25
26 struct SockAddr_tag {
27     char *error;
28     unsigned long address;
29 };
30
31 struct buffer {
32     struct buffer *next;
33     int buflen, bufpos;
34     char buf[BUFFER_GRANULE];
35 };
36
37 static tree234 *sktree;
38
39 static int cmpfortree(void *av, void *bv) {
40     Socket a = (Socket)av, b = (Socket)bv;
41     unsigned long as = (unsigned long)a->s, bs = (unsigned long)b->s;
42     if (as < bs) return -1;
43     if (as > bs) return +1;
44     return 0;
45 }
46
47 static int cmpforsearch(void *av, void *bv) {
48     Socket b = (Socket)bv;
49     unsigned long as = (unsigned long)av, bs = (unsigned long)b->s;
50     if (as < bs) return -1;
51     if (as > bs) return +1;
52     return 0;
53 }
54
55 void sk_init(void) {
56     sktree = newtree234(cmpfortree);
57 }
58
59 SockAddr sk_namelookup(char *host, char **canonicalname) {
60     SockAddr ret = smalloc(sizeof(struct SockAddr_tag));
61     unsigned long a;
62     struct hostent *h;
63
64     ret->error = NULL;
65     if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) {
66         if ( (h = gethostbyname(host)) == NULL) {
67             DWORD err = WSAGetLastError();
68             ret->error = (err == WSAENETDOWN ? "Network is down" :
69                           err == WSAHOST_NOT_FOUND ? "Host does not exist" :
70                           err == WSATRY_AGAIN ? "Host not found" :
71                           "gethostbyname: unknown error");
72         } else {
73             memcpy (&a, h->h_addr, sizeof(a));
74             *canonicalname = h->h_name;
75         }
76     } else {
77         *canonicalname = host;
78     }
79     ret->address = ntohl(a);
80
81     return ret;
82 }
83
84 void sk_addr_free(SockAddr addr) {
85     sfree(addr);
86 }
87
88 Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) {
89     SOCKET s;
90     SOCKADDR_IN a;
91     DWORD err;
92     char *errstr;
93     Socket ret;
94     extern char *do_select(SOCKET skt, int startup);
95
96     /*
97      * Create Socket structure.
98      */
99     ret = smalloc(sizeof(struct Socket_tag));
100     ret->error = NULL;
101     ret->receiver = receiver;
102     ret->head = ret->tail = NULL;
103     ret->writable = 1;                 /* to start with */
104     ret->in_oob = FALSE;
105     ret->sending_oob = 0;
106
107     /*
108      * Open socket.
109      */
110     s = socket(AF_INET, SOCK_STREAM, 0);
111     ret->s = s;
112
113     if (s == INVALID_SOCKET) {
114         err = WSAGetLastError();
115         ret->error = (err == WSAENETDOWN ? "Network is down" :
116                       err == WSAEAFNOSUPPORT ? "TCP/IP support not present" :
117                       "socket(): unknown error");
118         return ret;
119     }
120
121     /*
122      * Bind to local address.
123      */
124     a.sin_family = AF_INET;
125     a.sin_addr.s_addr = htonl(INADDR_ANY);
126     a.sin_port = htons(0);
127     if (bind (s, (struct sockaddr *)&a, sizeof(a)) == SOCKET_ERROR) {
128         err = WSAGetLastError();
129         ret->error = (err == WSAENETDOWN ? "Network is down" :
130                       "bind(): unknown error");
131         return ret;
132     }
133
134     /*
135      * Connect to remote address.
136      */
137     a.sin_addr.s_addr = htonl(addr->address);
138     a.sin_port = htons((short)port);
139     if (connect (s, (struct sockaddr *)&a, sizeof(a)) == SOCKET_ERROR) {
140         err = WSAGetLastError();
141         ret->error = (err == WSAENETDOWN ? "Network is down" :
142                       err == WSAECONNREFUSED ? "Connection refused" :
143                       err == WSAENETUNREACH ? "Network is unreachable" :
144                       err == WSAEHOSTUNREACH ? "No route to host" :
145                       "connect(): unknown error");
146         return ret;
147     }
148
149     /* Set up a select mechanism. This could be an AsyncSelect on a
150      * window, or an EventSelect on an event object. */
151     errstr = do_select(s, 1);
152     if (errstr) {
153         ret->error = errstr;
154         return ret;
155     }
156
157     add234(sktree, ret);
158
159     return ret;
160 }
161
162 void sk_close(Socket s) {
163     del234(sktree, s);
164     do_select(s->s, 0);
165     closesocket(s->s);
166     sfree(s);
167 }
168
169 char *winsock_error_string(int error) {
170     switch (error) {
171       case WSAEACCES: return "Network error: Permission denied";
172       case WSAEADDRINUSE: return "Network error: Address already in use";
173       case WSAEADDRNOTAVAIL: return "Network error: Cannot assign requested address";
174       case WSAEAFNOSUPPORT: return "Network error: Address family not supported by protocol family";
175       case WSAEALREADY: return "Network error: Operation already in progress";
176       case WSAECONNABORTED: return "Network error: Software caused connection abort";
177       case WSAECONNREFUSED: return "Network error: Connection refused";
178       case WSAECONNRESET: return "Network error: Connection reset by peer";
179       case WSAEDESTADDRREQ: return "Network error: Destination address required";
180       case WSAEFAULT: return "Network error: Bad address";
181       case WSAEHOSTDOWN: return "Network error: Host is down";
182       case WSAEHOSTUNREACH: return "Network error: No route to host";
183       case WSAEINPROGRESS: return "Network error: Operation now in progress";
184       case WSAEINTR: return "Network error: Interrupted function call";
185       case WSAEINVAL: return "Network error: Invalid argument";
186       case WSAEISCONN: return "Network error: Socket is already connected";
187       case WSAEMFILE: return "Network error: Too many open files";
188       case WSAEMSGSIZE: return "Network error: Message too long";
189       case WSAENETDOWN: return "Network error: Network is down";
190       case WSAENETRESET: return "Network error: Network dropped connection on reset";
191       case WSAENETUNREACH: return "Network error: Network is unreachable";
192       case WSAENOBUFS: return "Network error: No buffer space available";
193       case WSAENOPROTOOPT: return "Network error: Bad protocol option";
194       case WSAENOTCONN: return "Network error: Socket is not connected";
195       case WSAENOTSOCK: return "Network error: Socket operation on non-socket";
196       case WSAEOPNOTSUPP: return "Network error: Operation not supported";
197       case WSAEPFNOSUPPORT: return "Network error: Protocol family not supported";
198       case WSAEPROCLIM: return "Network error: Too many processes";
199       case WSAEPROTONOSUPPORT: return "Network error: Protocol not supported";
200       case WSAEPROTOTYPE: return "Network error: Protocol wrong type for socket";
201       case WSAESHUTDOWN: return "Network error: Cannot send after socket shutdown";
202       case WSAESOCKTNOSUPPORT: return "Network error: Socket type not supported";
203       case WSAETIMEDOUT: return "Network error: Connection timed out";
204       case WSAEWOULDBLOCK: return "Network error: Resource temporarily unavailable";
205       case WSAEDISCON: return "Network error: Graceful shutdown in progress";
206       default: return "Unknown network error";
207     }
208 }
209
210 /*
211  * The function which tries to send on a socket once it's deemed
212  * writable.
213  */
214 void try_send(Socket s) {
215     while (s->head) {
216         int nsent;
217         DWORD err;
218         int len, urgentflag;
219
220         if (s->sending_oob) {
221             urgentflag = MSG_OOB;
222             len = s->sending_oob;
223         } else {
224             urgentflag = 0;
225             len = s->head->buflen - s->head->bufpos;
226         }
227
228         nsent = send(s->s, s->head->buf + s->head->bufpos, len, urgentflag);
229         noise_ultralight(nsent);
230         if (nsent <= 0) {
231             err = (nsent < 0 ? WSAGetLastError() : 0);
232             if (err == WSAEWOULDBLOCK) {
233                 /* Perfectly normal: we've sent all we can for the moment. */
234                 s->writable = FALSE;
235                 return;
236             } else if (nsent == 0 ||
237                        err == WSAECONNABORTED ||
238                        err == WSAECONNRESET) {
239                 /*
240                  * FIXME. This will have to be done better when we
241                  * start managing multiple sockets (e.g. SSH port
242                  * forwarding), because if we get CONNRESET while
243                  * trying to write a particular forwarded socket
244                  * then it isn't necessarily the end of the world.
245                  * Ideally I'd like to pass the error code back to
246                  * somewhere the next select_result() will see it,
247                  * but that might be hard. Perhaps I should pass it
248                  * back to be queued in the Windows front end bit.
249                  */
250                 fatalbox(winsock_error_string(err));
251             } else {
252                 fatalbox(winsock_error_string(err));
253             }
254         } else {
255             s->head->bufpos += nsent;
256             if (s->sending_oob)
257                 s->sending_oob -= nsent;
258             if (s->head->bufpos >= s->head->buflen) {
259                 struct buffer *tmp = s->head;
260                 s->head = tmp->next;
261                 sfree(tmp);
262                 if (!s->head)
263                     s->tail = NULL;
264             }
265         }
266     }
267 }
268
269 void sk_write(Socket s, char *buf, int len) {
270     /*
271      * Add the data to the buffer list on the socket.
272      */
273     if (s->tail && s->tail->buflen < BUFFER_GRANULE) {
274         int copylen = min(len, BUFFER_GRANULE - s->tail->buflen);
275         memcpy(s->tail->buf + s->tail->buflen, buf, copylen);
276         buf += copylen;
277         len -= copylen;
278         s->tail->buflen += copylen;
279     }
280     while (len > 0) {
281         int grainlen = min(len, BUFFER_GRANULE);
282         struct buffer *newbuf;
283         newbuf = smalloc(sizeof(struct buffer));
284         newbuf->bufpos = 0;
285         newbuf->buflen = grainlen;
286         memcpy(newbuf->buf, buf, grainlen);
287         buf += grainlen;
288         len -= grainlen;
289         if (s->tail)
290             s->tail->next = newbuf;
291         else
292             s->head = s->tail = newbuf;
293         newbuf->next = NULL;
294         s->tail = newbuf;
295     }
296
297     /*
298      * Now try sending from the start of the buffer list.
299      */
300     if (s->writable)
301         try_send(s);
302 }
303
304 void sk_write_oob(Socket s, char *buf, int len) {
305     /*
306      * Replace the buffer list on the socket with the data.
307      */
308     if (!s->head) {
309         s->head = smalloc(sizeof(struct buffer));
310     } else {
311         struct buffer *walk = s->head->next;
312         while (walk) {
313             struct buffer *tmp = walk;
314             walk = tmp->next;
315             sfree(tmp);
316         }
317     }
318     s->head->next = NULL;
319     s->tail = s->head;
320     s->head->buflen = len;
321     memcpy(s->head->buf, buf, len);
322
323     /*
324      * Set the Urgent marker.
325      */
326     s->sending_oob = len;
327
328     /*
329      * Now try sending from the start of the buffer list.
330      */
331     if (s->writable)
332         try_send(s);
333 }
334
335 int select_result(WPARAM wParam, LPARAM lParam) {
336     int ret;
337     DWORD err;
338     char buf[BUFFER_GRANULE];
339     Socket s;
340     u_long atmark;
341
342     /* wParam is the socket itself */
343     s = find234(sktree, (void *)wParam, cmpforsearch);
344     if (!s)
345         return 1;                      /* boggle */
346
347     if ((err = WSAGETSELECTERROR(lParam)) != 0) {
348         fatalbox(winsock_error_string(err));
349     }
350
351     noise_ultralight(lParam);
352
353     switch (WSAGETSELECTEVENT(lParam)) {
354       case FD_READ:
355         ret = recv(s->s, buf, sizeof(buf), 0);
356         if (ret < 0) {
357             err = WSAGetLastError();
358             if (err == WSAEWOULDBLOCK) {
359                 break;
360             }
361         }
362         if (ret < 0) {
363             fatalbox(winsock_error_string(err));
364         } else {
365             int type = s->in_oob ? 2 : 0;
366             s->in_oob = FALSE;
367             return s->receiver(s, type, buf, ret);
368         }
369         break;
370       case FD_OOB:
371         /*
372          * Read all data up to the OOB marker, and send it to the
373          * receiver with urgent==1 (OOB pending).
374          */
375         atmark = 1;
376         s->in_oob = TRUE;
377         /* Some WinSock wrappers don't support this call, so we
378          * deliberately don't check the return value. If the call
379          * fails and does nothing, we will get back atmark==1,
380          * which is good enough to keep going at least. */
381         ioctlsocket(s->s, SIOCATMARK, &atmark);
382         ret = recv(s->s, buf, sizeof(buf), MSG_OOB);
383         noise_ultralight(ret);
384         if (ret <= 0) {
385             fatalbox(ret == 0 ? "Internal networking trouble" :
386                      winsock_error_string(WSAGetLastError()));
387         } else {
388             return s->receiver(s, atmark ? 2 : 1, buf, ret);
389         }
390         break;
391       case FD_WRITE:
392         s->writable = 1;
393         try_send(s);
394         break;
395       case FD_CLOSE:
396         /* Signal a close on the socket. */
397         return s->receiver(s, 0, NULL, 0);
398         break;
399     }
400
401     return 1;
402 }
403
404 /*
405  * Each socket abstraction contains a `void *' private field in
406  * which the client can keep state.
407  */
408 void sk_set_private_ptr(Socket s, void *ptr) {
409     s->private_ptr = ptr;
410 }
411 void *sk_get_private_ptr(Socket s) {
412     return s->private_ptr;
413 }
414
415 /*
416  * Special error values are returned from sk_namelookup and sk_new
417  * if there's a problem. These functions extract an error message,
418  * or return NULL if there's no problem.
419  */
420 char *sk_addr_error(SockAddr addr) {
421     return addr->error;
422 }
423 char *sk_socket_error(Socket s) {
424     return s->error;
425 }
426
427 /*
428  * For Plink: enumerate all sockets currently active.
429  */
430 SOCKET first_socket(enum234 *e) {
431     Socket s = first234(sktree, e);
432     return s ? s->s : INVALID_SOCKET;
433 }
434 SOCKET next_socket(enum234 *e) {
435     Socket s = next234(e);
436     return s ? s->s : INVALID_SOCKET;
437 }