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); |
190 | void ot_poll(void); |
191 | |
192 | Socket ot_register(void *sock, Plug plug) |
193 | { |
194 | static struct socket_function_table fn_table = { |
195 | ot_tcp_plug, |
196 | ot_tcp_close, |
197 | ot_tcp_write, |
198 | ot_tcp_write_oob, |
199 | ot_tcp_flush, |
200 | ot_tcp_set_private_ptr, |
201 | ot_tcp_get_private_ptr, |
202 | ot_tcp_set_frozen, |
203 | ot_tcp_socket_error |
204 | }; |
205 | |
206 | Actual_Socket ret; |
207 | |
34773273 |
208 | ret = snew(struct Socket_tag); |
27a3458f |
209 | ret->fn = &fn_table; |
b1234a5c |
210 | ret->error = kOTNoError; |
27a3458f |
211 | ret->plug = plug; |
212 | bufchain_init(&ret->output_data); |
213 | ret->writable = 1; /* to start with */ |
214 | ret->sending_oob = 0; |
215 | ret->frozen = 1; |
216 | ret->frozen_readable = 0; |
217 | ret->localhost_only = 0; /* unused, but best init anyway */ |
218 | ret->pending_error = 0; |
219 | ret->oobpending = FALSE; |
220 | ret->listener = 0; |
221 | |
222 | ret->ep = (EndpointRef)sock; |
223 | |
224 | /* some sort of error checking */ |
225 | |
226 | ret->oobinline = 0; |
227 | |
228 | /* Add this to the list of all sockets */ |
229 | ret->next = ot.socklist; |
230 | ret->prev = &ot.socklist; |
231 | ot.socklist = ret; |
232 | |
233 | return (Socket) ret; |
234 | } |
235 | |
236 | Socket ot_new(SockAddr addr, int port, int privport, int oobinline, |
79bf227b |
237 | int nodelay, int keepalive, Plug plug) |
27a3458f |
238 | { |
239 | static struct socket_function_table fn_table = { |
240 | ot_tcp_plug, |
241 | ot_tcp_close, |
242 | ot_tcp_write, |
243 | ot_tcp_write_oob, |
244 | ot_tcp_flush, |
245 | ot_tcp_set_private_ptr, |
246 | ot_tcp_get_private_ptr, |
247 | ot_tcp_set_frozen, |
248 | ot_tcp_socket_error |
249 | }; |
250 | |
251 | Actual_Socket ret; |
252 | EndpointRef ep; |
253 | OSStatus err; |
6f81f628 |
254 | InetAddress dest; |
27a3458f |
255 | TCall connectCall; |
256 | |
34773273 |
257 | ret = snew(struct Socket_tag); |
27a3458f |
258 | ret->fn = &fn_table; |
b1234a5c |
259 | ret->error = kOTNoError; |
27a3458f |
260 | ret->plug = plug; |
261 | bufchain_init(&ret->output_data); |
262 | ret->connected = 0; /* to start with */ |
263 | ret->writable = 0; /* to start with */ |
264 | ret->sending_oob = 0; |
265 | ret->frozen = 0; |
266 | ret->frozen_readable = 0; |
267 | ret->localhost_only = 0; /* unused, but best init anyway */ |
268 | ret->pending_error = 0; |
269 | ret->oobinline = oobinline; |
270 | ret->oobpending = FALSE; |
271 | ret->listener = 0; |
272 | |
273 | /* Open Endpoint, configure it for TCP over anything */ |
274 | |
275 | ep = OTOpenEndpoint(OTCreateConfiguration("tcp"), 0, NULL, &err); |
276 | |
277 | ret->ep = ep; |
278 | |
279 | if (err) { |
b1234a5c |
280 | ret->error = err; |
27a3458f |
281 | return (Socket) ret; |
282 | } |
283 | |
79bf227b |
284 | /* TODO: oobinline, nodelay, keepalive */ |
27a3458f |
285 | |
286 | /* |
287 | * Bind to local address. |
288 | */ |
289 | |
290 | /* FIXME: pay attention to privport */ |
291 | |
292 | err = OTBind(ep, NULL, NULL); /* OpenTransport always picks our address */ |
293 | |
294 | if (err) { |
b1234a5c |
295 | ret->error = err; |
27a3458f |
296 | return (Socket) ret; |
297 | } |
298 | |
299 | /* |
300 | * Connect to remote address. |
301 | */ |
302 | |
6f81f628 |
303 | /* XXX Try non-primary addresses */ |
304 | OTInitInetAddress(&dest, port, addr->hostinfo.addrs[0]); |
27a3458f |
305 | |
40d38714 |
306 | memset(&connectCall, 0, sizeof(TCall)); |
6f81f628 |
307 | connectCall.addr.buf = (UInt8 *) &dest; |
308 | connectCall.addr.len = sizeof(dest); |
27a3458f |
309 | |
310 | err = OTConnect(ep, &connectCall, nil); |
311 | |
312 | if (err) { |
b1234a5c |
313 | ret->error = err; |
27a3458f |
314 | return (Socket) ret; |
315 | } else { |
316 | ret->connected = 1; |
317 | ret->writable = 1; |
318 | } |
319 | |
320 | /* Add this to the list of all sockets */ |
321 | ret->next = ot.socklist; |
322 | ret->prev = &ot.socklist; |
b89053c0 |
323 | if (ret->next != NULL) |
324 | ret->next->prev = &ret->next; |
27a3458f |
325 | ot.socklist = ret; |
326 | |
f85e6f6e |
327 | /* XXX: don't know whether we can sk_addr_free(addr); */ |
328 | |
27a3458f |
329 | return (Socket) ret; |
330 | } |
331 | |
332 | Socket ot_newlistener(char *foobar, int port, Plug plug, int local_host_only) |
333 | { |
334 | Actual_Socket s; |
335 | |
336 | return (Socket) s; |
337 | } |
338 | |
339 | static void ot_tcp_close(Socket sock) |
340 | { |
341 | Actual_Socket s = (Actual_Socket) sock; |
342 | |
343 | OTCloseProvider(s->ep); |
344 | |
345 | /* Unhitch from list of sockets */ |
346 | *s->prev = s->next; |
347 | if (s->next != NULL) |
348 | s->next->prev = s->prev; |
349 | |
350 | sfree(s); |
351 | } |
352 | |
353 | static void try_send(Actual_Socket s) |
354 | { |
355 | while (bufchain_size(&s->output_data) > 0) { |
356 | int nsent; |
357 | void *data; |
358 | int len; |
359 | |
360 | /* Don't care about oob right now */ |
361 | |
362 | bufchain_prefix(&s->output_data, &data, &len); |
363 | |
364 | nsent = OTSnd(s->ep, data, len, 0); |
365 | noise_ultralight(nsent); |
366 | |
367 | if (nsent <= 0) { |
368 | /* something bad happened, hey ho */ |
369 | } else { |
370 | /* still don't care about oob */ |
371 | bufchain_consume(&s->output_data, nsent); |
372 | } |
373 | } |
374 | } |
375 | |
376 | static int ot_tcp_write(Socket sock, char const *buf, int len) |
377 | { |
378 | Actual_Socket s = (Actual_Socket) sock; |
379 | |
380 | bufchain_add(&s->output_data, buf, len); |
381 | |
382 | if (s->writable) |
383 | try_send(s); |
384 | return bufchain_size(&s->output_data); |
385 | } |
386 | |
387 | static int ot_tcp_write_oob(Socket sock, char const *buf, int len) |
388 | { |
389 | /* Don't care about oob */ |
390 | return 0; |
391 | } |
392 | |
393 | |
394 | /* |
395 | * Each socket abstraction contains a `void *' private field in |
396 | * which the client can keep state. |
397 | */ |
398 | static void ot_tcp_set_private_ptr(Socket sock, void *ptr) |
399 | { |
400 | Actual_Socket s = (Actual_Socket) sock; |
401 | s->private_ptr = ptr; |
402 | } |
403 | |
404 | static void *ot_tcp_get_private_ptr(Socket sock) |
405 | { |
406 | Actual_Socket s = (Actual_Socket) sock; |
407 | return s->private_ptr; |
408 | } |
409 | |
410 | |
411 | /* |
412 | * Special error values are returned from ot_namelookup and ot_new |
413 | * if there's a problem. These functions extract an error message, |
414 | * or return NULL if there's no problem. |
415 | */ |
416 | char *ot_addr_error(SockAddr addr) |
417 | { |
b1234a5c |
418 | static char buf[128]; |
419 | |
420 | if (addr->error == kOTNoError) |
421 | return NULL; |
422 | sprintf(buf, "error %d", addr->error); |
423 | return buf; |
27a3458f |
424 | } |
cbe2d68f |
425 | static const char *ot_tcp_socket_error(Socket sock) |
27a3458f |
426 | { |
427 | Actual_Socket s = (Actual_Socket) sock; |
b1234a5c |
428 | static char buf[128]; |
429 | |
430 | if (s->error == kOTNoError) |
431 | return NULL; |
432 | sprintf(buf, "error %d", s->error); |
433 | return buf; |
27a3458f |
434 | } |
435 | |
436 | static void ot_tcp_set_frozen(Socket sock, int is_frozen) |
437 | { |
438 | Actual_Socket s = (Actual_Socket) sock; |
439 | |
440 | if (s->frozen == is_frozen) |
441 | return; |
442 | s->frozen = is_frozen; |
443 | } |
444 | |
445 | /* |
446 | * Poll all our sockets from an event loop |
447 | */ |
448 | |
449 | void ot_poll(void) |
450 | { |
451 | Actual_Socket s; |
452 | OTResult o; |
453 | |
454 | for (s = ot.socklist; s != NULL; s = s->next) { |
455 | o = OTLook(s->ep); |
456 | |
457 | switch(o) { |
458 | case T_DATA: /* Normal Data */ |
459 | ot_recv(s); |
460 | break; |
461 | case T_EXDATA: /* Expedited Data (urgent?) */ |
462 | ot_recv(s); |
463 | break; |
464 | } |
465 | } |
466 | } |
467 | |
468 | void ot_recv(Actual_Socket s) |
469 | { |
470 | OTResult o; |
eabfda2d |
471 | char buf[2048]; |
27a3458f |
472 | OTFlags flags; |
473 | |
474 | if (s->frozen) return; |
475 | |
eabfda2d |
476 | o = OTRcv(s->ep, buf, sizeof(buf), &flags); |
477 | if (o > 0) |
478 | plug_receive(s->plug, 0, buf, o); |
479 | if (o < 0 && o != kOTNoDataErr) |
480 | plug_closing(s->plug, NULL, 0, 0); /* XXX Error msg */ |
27a3458f |
481 | } |
482 | |
483 | |
484 | /* |
485 | * Local Variables: |
486 | * c-file-style: "simon" |
487 | * End: |
488 | */ |