27a3458f |
1 | /* |
2 | * Macintosh OpenTransport networking abstraction |
3 | */ |
4 | |
c9446101 |
5 | #if TARGET_API_MAC_CARBON |
6 | #define OTCARBONAPPLICATION 1 |
7 | #endif |
8 | |
7bd5e05c |
9 | #include <Files.h> /* Needed by OpenTransportInternet.h */ |
27a3458f |
10 | #include <OpenTransport.h> |
11 | #include <OpenTptInternet.h> |
12 | |
40d38714 |
13 | #include <string.h> |
14 | |
27a3458f |
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... */ |
b1234a5c |
23 | OSStatus error; |
27a3458f |
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 { |
6f81f628 |
47 | int resolved; |
b1234a5c |
48 | OSStatus error; |
6f81f628 |
49 | InetHostInfo hostinfo; |
50 | char hostname[512]; |
27a3458f |
51 | }; |
52 | |
53 | /* Globals */ |
54 | |
55 | static struct { |
56 | Actual_Socket socklist; |
6f81f628 |
57 | InetSvcRef inetsvc; |
27a3458f |
58 | } ot; |
59 | |
60 | OSErr ot_init(void) |
61 | { |
6f81f628 |
62 | OSStatus err; |
63 | |
64 | err = InitOpenTransport(); |
65 | if (err != kOTNoError) return err; |
66 | ot.inetsvc = OTOpenInternetServices(kDefaultInternetServicesPath, 0, &err); |
67 | return err; |
27a3458f |
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 | |
2e96d504 |
82 | SockAddr ot_namelookup(char const *host, char **canonicalname) |
27a3458f |
83 | { |
34773273 |
84 | SockAddr ret = snew(struct SockAddr_tag); |
6f81f628 |
85 | char *realhost; |
2e96d504 |
86 | |
87 | /* Casting away const -- hope OTInetStringToAddress is sensible */ |
88 | ret->error = OTInetStringToAddress(ot.inetsvc, (char *)host, |
89 | &ret->hostinfo); |
6f81f628 |
90 | ret->resolved = TRUE; |
91 | |
92 | if (ret->error == kOTNoError) |
93 | realhost = ret->hostinfo.name; |
94 | else |
95 | realhost = ""; |
34773273 |
96 | *canonicalname = snewn(1+strlen(realhost), char); |
6f81f628 |
97 | strcpy(*canonicalname, realhost); |
27a3458f |
98 | return ret; |
99 | } |
100 | |
2e96d504 |
101 | SockAddr ot_nonamelookup(char const *host) |
27a3458f |
102 | { |
34773273 |
103 | SockAddr ret = snew(struct SockAddr_tag); |
27a3458f |
104 | |
6f81f628 |
105 | ret->resolved = FALSE; |
106 | ret->error = kOTNoError; |
107 | ret->hostname[0] = '\0'; |
108 | strncat(ret->hostname, host, lenof(ret->hostname) - 1); |
27a3458f |
109 | return ret; |
110 | } |
111 | |
112 | void ot_getaddr(SockAddr addr, char *buf, int buflen) |
113 | { |
6f81f628 |
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); |
27a3458f |
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 | { |
6f81f628 |
135 | int i; |
27a3458f |
136 | |
6f81f628 |
137 | if (addr->resolved) |
138 | for (i = 0; i < kMaxHostAddrs; i++) |
139 | if (addr->hostinfo.addrs[i] & 0xff000000 == 0x7f000000) |
140 | return TRUE; |
27a3458f |
141 | return FALSE; |
142 | } |
143 | |
144 | int ot_addrtype(SockAddr addr) |
145 | { |
6f81f628 |
146 | |
147 | if (addr->resolved) |
148 | return ADDRTYPE_IPV4; |
149 | return ADDRTYPE_NAME; |
27a3458f |
150 | } |
151 | |
152 | void ot_addrcopy(SockAddr addr, char *buf) |
153 | { |
6f81f628 |
154 | |
155 | /* XXX only return first address */ |
156 | memcpy(buf, &addr->hostinfo.addrs[0], 4); |
27a3458f |
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); |
cbe2d68f |
188 | static const char *ot_tcp_socket_error(Socket s); |
27a3458f |
189 | static void ot_recv(Actual_Socket s); |
a53784ab |
190 | static void ot_listenaccept(Actual_Socket s); |
27a3458f |
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 | |
34773273 |
209 | ret = snew(struct Socket_tag); |
27a3458f |
210 | ret->fn = &fn_table; |
b1234a5c |
211 | ret->error = kOTNoError; |
27a3458f |
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, |
79bf227b |
238 | int nodelay, int keepalive, Plug plug) |
27a3458f |
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; |
6f81f628 |
255 | InetAddress dest; |
27a3458f |
256 | TCall connectCall; |
257 | |
34773273 |
258 | ret = snew(struct Socket_tag); |
27a3458f |
259 | ret->fn = &fn_table; |
b1234a5c |
260 | ret->error = kOTNoError; |
27a3458f |
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) { |
b1234a5c |
281 | ret->error = err; |
27a3458f |
282 | return (Socket) ret; |
283 | } |
284 | |
79bf227b |
285 | /* TODO: oobinline, nodelay, keepalive */ |
27a3458f |
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) { |
b1234a5c |
296 | ret->error = err; |
27a3458f |
297 | return (Socket) ret; |
298 | } |
299 | |
300 | /* |
301 | * Connect to remote address. |
302 | */ |
303 | |
6f81f628 |
304 | /* XXX Try non-primary addresses */ |
305 | OTInitInetAddress(&dest, port, addr->hostinfo.addrs[0]); |
27a3458f |
306 | |
40d38714 |
307 | memset(&connectCall, 0, sizeof(TCall)); |
6f81f628 |
308 | connectCall.addr.buf = (UInt8 *) &dest; |
309 | connectCall.addr.len = sizeof(dest); |
27a3458f |
310 | |
311 | err = OTConnect(ep, &connectCall, nil); |
312 | |
313 | if (err) { |
b1234a5c |
314 | ret->error = err; |
27a3458f |
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; |
b89053c0 |
324 | if (ret->next != NULL) |
325 | ret->next->prev = &ret->next; |
27a3458f |
326 | ot.socklist = ret; |
327 | |
f85e6f6e |
328 | /* XXX: don't know whether we can sk_addr_free(addr); */ |
329 | |
27a3458f |
330 | return (Socket) ret; |
331 | } |
332 | |
a53784ab |
333 | Socket ot_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, |
334 | int address_family) |
27a3458f |
335 | { |
a53784ab |
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; |
27a3458f |
404 | |
a53784ab |
405 | return (Socket) ret; |
27a3458f |
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 | { |
b1234a5c |
487 | static char buf[128]; |
488 | |
489 | if (addr->error == kOTNoError) |
490 | return NULL; |
491 | sprintf(buf, "error %d", addr->error); |
492 | return buf; |
27a3458f |
493 | } |
cbe2d68f |
494 | static const char *ot_tcp_socket_error(Socket sock) |
27a3458f |
495 | { |
496 | Actual_Socket s = (Actual_Socket) sock; |
b1234a5c |
497 | static char buf[128]; |
498 | |
499 | if (s->error == kOTNoError) |
500 | return NULL; |
501 | sprintf(buf, "error %d", s->error); |
502 | return buf; |
27a3458f |
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; |
a53784ab |
533 | case T_LISTEN: /* Connection attempt */ |
534 | ot_listenaccept(s); |
535 | break; |
27a3458f |
536 | } |
537 | } |
538 | } |
539 | |
540 | void ot_recv(Actual_Socket s) |
541 | { |
542 | OTResult o; |
eabfda2d |
543 | char buf[2048]; |
27a3458f |
544 | OTFlags flags; |
545 | |
546 | if (s->frozen) return; |
547 | |
eabfda2d |
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 */ |
27a3458f |
553 | } |
554 | |
a53784ab |
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; |
27a3458f |
569 | |
a53784ab |
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 | |
27a3458f |
585 | /* |
586 | * Local Variables: |
587 | * c-file-style: "simon" |
588 | * End: |
589 | */ |