Cleanups of the GSSAPI support. On Windows, standard GSS libraries
[u/mdw/putty] / mac / otnet.c
1 /*
2 * Macintosh OpenTransport networking abstraction
3 */
4
5 #if TARGET_API_MAC_CARBON
6 #define OTCARBONAPPLICATION 1
7 #endif
8
9 #include <Files.h> /* Needed by OpenTransportInternet.h */
10 #include <OpenTransport.h>
11 #include <OpenTptInternet.h>
12
13 #include <string.h>
14
15 #define DEFINE_PLUG_METHOD_MACROS
16 #include "putty.h"
17 #include "network.h"
18 #include "mac.h"
19
20 struct Socket_tag {
21 struct socket_function_table *fn;
22 /* other stuff... */
23 OSStatus error;
24 EndpointRef ep;
25 Plug plug;
26 void *private_ptr;
27 bufchain output_data;
28 int connected;
29 int writable;
30 int frozen; /* this causes readability notifications to be ignored */
31 int frozen_readable; /* this means we missed at least one readability
32 * notification while we were frozen */
33 int localhost_only; /* for listening sockets */
34 char oobdata[1];
35 int sending_oob;
36 int oobpending; /* is there OOB data available to read?*/
37 int oobinline;
38 int pending_error; /* in case send() returns error */
39 int listener;
40 int nodelay, keepalive;
41 int privport, port;
42 struct Socket_tag *next;
43 struct Socket_tag **prev;
44 };
45
46 typedef struct Socket_tag *Actual_Socket;
47
48 struct SockAddr_tag {
49 int resolved;
50 OSStatus error;
51 InetHostInfo hostinfo;
52 char hostname[512];
53 };
54
55 /* Globals */
56
57 static struct {
58 Actual_Socket socklist;
59 InetSvcRef inetsvc;
60 } ot;
61
62 OSErr ot_init(void)
63 {
64 OSStatus err;
65
66 err = InitOpenTransport();
67 if (err != kOTNoError) return err;
68 ot.inetsvc = OTOpenInternetServices(kDefaultInternetServicesPath, 0, &err);
69 return err;
70 }
71
72 void ot_cleanup(void)
73 {
74 Actual_Socket s;
75
76 for (s = ot.socklist; s !=NULL; s = s->next) {
77 OTUnbind(s->ep);
78 OTCloseProvider(s->ep);
79 }
80
81 CloseOpenTransport();
82 }
83
84 SockAddr ot_namelookup(char const *host, char **canonicalname)
85 {
86 SockAddr ret = snew(struct SockAddr_tag);
87 char *realhost;
88
89 /* Casting away const -- hope OTInetStringToAddress is sensible */
90 ret->error = OTInetStringToAddress(ot.inetsvc, (char *)host,
91 &ret->hostinfo);
92 ret->resolved = TRUE;
93
94 if (ret->error == kOTNoError)
95 realhost = ret->hostinfo.name;
96 else
97 realhost = "";
98 *canonicalname = snewn(1+strlen(realhost), char);
99 strcpy(*canonicalname, realhost);
100 return ret;
101 }
102
103 SockAddr ot_nonamelookup(char const *host)
104 {
105 SockAddr ret = snew(struct SockAddr_tag);
106
107 ret->resolved = FALSE;
108 ret->error = kOTNoError;
109 ret->hostname[0] = '\0';
110 strncat(ret->hostname, host, lenof(ret->hostname) - 1);
111 return ret;
112 }
113
114 void ot_getaddr(SockAddr addr, char *buf, int buflen)
115 {
116 char mybuf[16];
117
118 buf[0] = '\0';
119 if (addr->resolved) {
120 /* XXX only return first address */
121 OTInetHostToString(addr->hostinfo.addrs[0], mybuf);
122 strncat(buf, mybuf, buflen - 1);
123 } else
124 strncat(buf, addr->hostname, buflen - 1);
125 }
126
127 /* I think "local" here really means "loopback" */
128
129 int ot_hostname_is_local(char *name)
130 {
131
132 return !strcmp(name, "localhost");
133 }
134
135 int ot_address_is_local(SockAddr addr)
136 {
137 int i;
138
139 if (addr->resolved)
140 for (i = 0; i < kMaxHostAddrs; i++)
141 if (addr->hostinfo.addrs[i] & 0xff000000 == 0x7f000000)
142 return TRUE;
143 return FALSE;
144 }
145
146 int ot_addrtype(SockAddr addr)
147 {
148
149 if (addr->resolved)
150 return ADDRTYPE_IPV4;
151 return ADDRTYPE_NAME;
152 }
153
154 void ot_addrcopy(SockAddr addr, char *buf)
155 {
156
157 /* XXX only return first address */
158 memcpy(buf, &addr->hostinfo.addrs[0], 4);
159 }
160
161 void ot_addr_free(SockAddr addr)
162 {
163 sfree(addr);
164 }
165
166
167 static Plug ot_tcp_plug(Socket sock, Plug p)
168 {
169 Actual_Socket s = (Actual_Socket) sock;
170 Plug ret = s->plug;
171 if (p)
172 s->plug = p;
173 return ret;
174 }
175
176 static void ot_tcp_flush(Socket s)
177 {
178 /*
179 * We send data to the socket as soon as we can anyway,
180 * so we don't need to do anything here. :-)
181 */
182 }
183
184 static void ot_tcp_close(Socket s);
185 static int ot_tcp_write(Socket s, char const *data, int len);
186 static int ot_tcp_write_oob(Socket s, char const *data, int len);
187 static void ot_tcp_set_private_ptr(Socket s, void *ptr);
188 static void *ot_tcp_get_private_ptr(Socket s);
189 static void ot_tcp_set_frozen(Socket s, int is_frozen);
190 static const char *ot_tcp_socket_error(Socket s);
191 static void ot_recv(Actual_Socket s);
192 static void ot_listenaccept(Actual_Socket s);
193 static void ot_setoption(EndpointRef, OTXTILevel, OTXTIName, UInt32);
194 void ot_poll(void);
195
196
197 Socket ot_register(void *sock, Plug plug)
198 {
199 static struct socket_function_table fn_table = {
200 ot_tcp_plug,
201 ot_tcp_close,
202 ot_tcp_write,
203 ot_tcp_write_oob,
204 ot_tcp_flush,
205 ot_tcp_set_private_ptr,
206 ot_tcp_get_private_ptr,
207 ot_tcp_set_frozen,
208 ot_tcp_socket_error
209 };
210
211 Actual_Socket ret;
212
213 ret = snew(struct Socket_tag);
214 ret->fn = &fn_table;
215 ret->error = kOTNoError;
216 ret->plug = plug;
217 bufchain_init(&ret->output_data);
218 ret->writable = 1; /* to start with */
219 ret->sending_oob = 0;
220 ret->frozen = 1;
221 ret->frozen_readable = 0;
222 ret->localhost_only = 0; /* unused, but best init anyway */
223 ret->pending_error = 0;
224 ret->oobpending = FALSE;
225 ret->listener = 0;
226
227 ret->ep = (EndpointRef)sock;
228
229 /* some sort of error checking */
230
231 ret->oobinline = 0;
232
233 /* Add this to the list of all sockets */
234 ret->next = ot.socklist;
235 ret->prev = &ot.socklist;
236 ot.socklist = ret;
237
238 return (Socket) ret;
239 }
240
241 Socket ot_new(SockAddr addr, int port, int privport, int oobinline,
242 int nodelay, int keepalive, Plug plug)
243 {
244 static struct socket_function_table fn_table = {
245 ot_tcp_plug,
246 ot_tcp_close,
247 ot_tcp_write,
248 ot_tcp_write_oob,
249 ot_tcp_flush,
250 ot_tcp_set_private_ptr,
251 ot_tcp_get_private_ptr,
252 ot_tcp_set_frozen,
253 ot_tcp_socket_error
254 };
255
256 Actual_Socket ret;
257 EndpointRef ep;
258 OSStatus err;
259 InetAddress dest;
260 TCall connectCall;
261
262 ret = snew(struct Socket_tag);
263 ret->fn = &fn_table;
264 ret->error = kOTNoError;
265 ret->plug = plug;
266 bufchain_init(&ret->output_data);
267 ret->connected = 0; /* to start with */
268 ret->writable = 0; /* to start with */
269 ret->sending_oob = 0;
270 ret->frozen = 0;
271 ret->frozen_readable = 0;
272 ret->localhost_only = 0; /* unused, but best init anyway */
273 ret->pending_error = 0;
274 ret->oobinline = oobinline;
275 ret->nodelay = nodelay;
276 ret->keepalive = keepalive;
277 ret->oobpending = FALSE;
278 ret->listener = 0;
279
280 /* Open Endpoint, configure it for TCP over anything */
281
282 ep = OTOpenEndpoint(OTCreateConfiguration("tcp"), 0, NULL, &err);
283
284 ret->ep = ep;
285
286 if (err) {
287 ret->error = err;
288 return (Socket) ret;
289 }
290
291 if (ret->oobinline)
292 ot_setoption(ep, INET_TCP, TCP_OOBINLINE, T_YES);
293
294 if (ret->nodelay)
295 ot_setoption(ep, INET_TCP, TCP_NODELAY, T_YES);
296
297 if (ret->keepalive) {
298 ot_setoption(ep, INET_TCP, TCP_KEEPALIVE, T_YES);
299 }
300
301 /*
302 * Bind to local address.
303 */
304
305 /* FIXME: pay attention to privport */
306
307 err = OTBind(ep, NULL, NULL); /* OpenTransport always picks our address */
308
309 if (err) {
310 ret->error = err;
311 return (Socket) ret;
312 }
313
314 /*
315 * Connect to remote address.
316 */
317
318 /* XXX Try non-primary addresses */
319 OTInitInetAddress(&dest, port, addr->hostinfo.addrs[0]);
320
321 memset(&connectCall, 0, sizeof(TCall));
322 connectCall.addr.buf = (UInt8 *) &dest;
323 connectCall.addr.len = sizeof(dest);
324
325 err = OTConnect(ep, &connectCall, nil);
326
327 if (err) {
328 ret->error = err;
329 return (Socket) ret;
330 } else {
331 ret->connected = 1;
332 ret->writable = 1;
333 }
334
335 /* Add this to the list of all sockets */
336 ret->next = ot.socklist;
337 ret->prev = &ot.socklist;
338 if (ret->next != NULL)
339 ret->next->prev = &ret->next;
340 ot.socklist = ret;
341
342 /* XXX: don't know whether we can sk_addr_free(addr); */
343
344 return (Socket) ret;
345 }
346
347 Socket ot_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
348 int address_family)
349 {
350 static struct socket_function_table fn_table = {
351 ot_tcp_plug,
352 ot_tcp_close,
353 ot_tcp_write,
354 ot_tcp_write_oob,
355 ot_tcp_flush,
356 ot_tcp_set_private_ptr,
357 ot_tcp_get_private_ptr,
358 ot_tcp_set_frozen,
359 ot_tcp_socket_error
360 };
361
362 Actual_Socket ret;
363 EndpointRef ep;
364 OSStatus err;
365 InetAddress addr;
366 TBind tbind;
367
368 ret = snew(struct Socket_tag);
369 ret->fn = &fn_table;
370 ret->error = kOTNoError;
371 ret->plug = plug;
372 bufchain_init(&ret->output_data);
373 ret->writable = 0; /* to start with */
374 ret->sending_oob = 0;
375 ret->frozen = 0;
376 ret->frozen_readable = 0;
377 ret->localhost_only = local_host_only;
378 ret->pending_error = 0;
379 ret->oobinline = 0;
380 ret->oobpending = FALSE;
381 ret->listener = 1;
382
383 /* Open Endpoint, configure it for TCP over anything, and load the
384 * tilisten module to serialize multiple simultaneous
385 * connections. */
386
387 ep = OTOpenEndpoint(OTCreateConfiguration("tilisten,tcp"), 0, NULL, &err);
388
389 ret->ep = ep;
390
391 if (err) {
392 ret->error = err;
393 return (Socket) ret;
394 }
395
396 ot_setoption(ep, INET_IP, IP_REUSEADDR, T_YES);
397
398 OTInitInetAddress(&addr, port, kOTAnyInetAddress);
399 /* XXX: pay attention to local_host_only */
400
401 tbind.addr.buf = (UInt8 *) &addr;
402 tbind.addr.len = sizeof(addr);
403 tbind.qlen = 10;
404
405 err = OTBind(ep, &tbind, NULL); /* XXX: check qlen we got */
406
407 if (err) {
408 ret->error = err;
409 return (Socket) ret;
410 }
411
412 /* Add this to the list of all sockets */
413 ret->next = ot.socklist;
414 ret->prev = &ot.socklist;
415 if (ret->next != NULL)
416 ret->next->prev = &ret->next;
417 ot.socklist = ret;
418
419 return (Socket) ret;
420 }
421
422 static void ot_tcp_close(Socket sock)
423 {
424 Actual_Socket s = (Actual_Socket) sock;
425
426 OTCloseProvider(s->ep);
427
428 /* Unhitch from list of sockets */
429 *s->prev = s->next;
430 if (s->next != NULL)
431 s->next->prev = s->prev;
432
433 sfree(s);
434 }
435
436 static void try_send(Actual_Socket s)
437 {
438 while (bufchain_size(&s->output_data) > 0) {
439 int nsent;
440 void *data;
441 int len;
442
443 /* Don't care about oob right now */
444
445 bufchain_prefix(&s->output_data, &data, &len);
446
447 nsent = OTSnd(s->ep, data, len, 0);
448 noise_ultralight(nsent);
449
450 if (nsent <= 0) {
451 /* something bad happened, hey ho */
452 } else {
453 /* still don't care about oob */
454 bufchain_consume(&s->output_data, nsent);
455 }
456 }
457 }
458
459 static int ot_tcp_write(Socket sock, char const *buf, int len)
460 {
461 Actual_Socket s = (Actual_Socket) sock;
462
463 bufchain_add(&s->output_data, buf, len);
464
465 if (s->writable)
466 try_send(s);
467 return bufchain_size(&s->output_data);
468 }
469
470 static int ot_tcp_write_oob(Socket sock, char const *buf, int len)
471 {
472 /* Don't care about oob */
473 return 0;
474 }
475
476
477 /*
478 * Each socket abstraction contains a `void *' private field in
479 * which the client can keep state.
480 */
481 static void ot_tcp_set_private_ptr(Socket sock, void *ptr)
482 {
483 Actual_Socket s = (Actual_Socket) sock;
484 s->private_ptr = ptr;
485 }
486
487 static void *ot_tcp_get_private_ptr(Socket sock)
488 {
489 Actual_Socket s = (Actual_Socket) sock;
490 return s->private_ptr;
491 }
492
493
494 /*
495 * Special error values are returned from ot_namelookup and ot_new
496 * if there's a problem. These functions extract an error message,
497 * or return NULL if there's no problem.
498 */
499 char *ot_addr_error(SockAddr addr)
500 {
501 static char buf[128];
502
503 if (addr->error == kOTNoError)
504 return NULL;
505 sprintf(buf, "error %d", addr->error);
506 return buf;
507 }
508 static const char *ot_tcp_socket_error(Socket sock)
509 {
510 Actual_Socket s = (Actual_Socket) sock;
511 static char buf[128];
512
513 if (s->error == kOTNoError)
514 return NULL;
515 sprintf(buf, "error %d", s->error);
516 return buf;
517 }
518
519 static void ot_tcp_set_frozen(Socket sock, int is_frozen)
520 {
521 Actual_Socket s = (Actual_Socket) sock;
522
523 if (s->frozen == is_frozen)
524 return;
525 s->frozen = is_frozen;
526 }
527
528 /*
529 * Poll all our sockets from an event loop
530 */
531
532 void ot_poll(void)
533 {
534 Actual_Socket s;
535 OTResult o;
536
537 for (s = ot.socklist; s != NULL; s = s->next) {
538 o = OTLook(s->ep);
539
540 switch(o) {
541 case T_DATA: /* Normal Data */
542 ot_recv(s);
543 break;
544 case T_EXDATA: /* Expedited Data (urgent?) */
545 ot_recv(s);
546 break;
547 case T_LISTEN: /* Connection attempt */
548 ot_listenaccept(s);
549 break;
550 case T_ORDREL: /* Orderly disconnect */
551 plug_closing(s->plug, NULL, 0, 0);
552 break;
553 case T_DISCONNECT: /* Abortive disconnect*/
554 plug_closing(s->plug, NULL, 0, 0);
555 break;
556 }
557 }
558 }
559
560 void ot_recv(Actual_Socket s)
561 {
562 OTResult o;
563 char buf[2048];
564 OTFlags flags;
565
566 if (s->frozen) return;
567
568 o = OTRcv(s->ep, buf, sizeof(buf), &flags);
569 if (o > 0)
570 plug_receive(s->plug, 0, buf, o);
571 if (o < 0 && o != kOTNoDataErr)
572 plug_closing(s->plug, NULL, 0, 0); /* XXX Error msg */
573 }
574
575 void ot_listenaccept(Actual_Socket s)
576 {
577 OTResult o;
578 OSStatus err;
579 InetAddress remoteaddr;
580 TCall tcall;
581 EndpointRef ep;
582
583 tcall.addr.maxlen = sizeof(InetAddress);
584 tcall.addr.buf = (unsigned char *)&remoteaddr;
585 tcall.opt.maxlen = 0;
586 tcall.opt.buf = NULL;
587 tcall.udata.maxlen = 0;
588 tcall.udata.buf = NULL;
589
590 o = OTListen(s->ep, &tcall);
591
592 if (o != kOTNoError)
593 return;
594
595 /* We've found an incoming connection, accept it */
596
597 ep = OTOpenEndpoint(OTCreateConfiguration("tcp"), 0, NULL, &err);
598 o = OTAccept(s->ep, ep, &tcall);
599 if (plug_accepting(s->plug, ep)) {
600 OTUnbind(ep);
601 OTCloseProvider(ep);
602 }
603 }
604
605 static void ot_setoption(EndpointRef ep,
606 OTXTILevel level,
607 OTXTIName name,
608 UInt32 value)
609 {
610 TOption option;
611 TOptMgmt request;
612 TOptMgmt result;
613
614 if (name == TCP_KEEPALIVE) {
615 option.len = sizeof(struct t_kpalive);
616 option.value[1] = T_UNSPEC;
617 } else
618 option.len = kOTFourByteOptionSize;
619 option.level = level;
620 option.name = name;
621 option.status = 0;
622 option.value[0] = value;
623
624 request.opt.buf = (unsigned char *) &option;
625 request.opt.len = sizeof(option);
626 request.flags = T_NEGOTIATE;
627
628 result.opt.buf = (unsigned char *) &option;
629 result.opt.maxlen = sizeof(option);
630
631 OTOptionManagement(ep, &request, &result);
632 }
633
634 /*
635 * Local Variables:
636 * c-file-style: "simon"
637 * End:
638 */