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