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