]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/otnet.c
Cleanups of the GSSAPI support. On Windows, standard GSS libraries
[PuTTY.git] / 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  */