27a3458f |
1 | /* |
2 | * Macintosh OpenTransport networking abstraction |
3 | */ |
4 | |
5 | #include <OpenTransport.h> |
6 | #include <OpenTptInternet.h> |
7 | |
40d38714 |
8 | #include <string.h> |
9 | |
27a3458f |
10 | #define DEFINE_PLUG_METHOD_MACROS |
11 | #include "putty.h" |
12 | #include "network.h" |
13 | #include "mac.h" |
14 | |
15 | struct Socket_tag { |
16 | struct socket_function_table *fn; |
17 | /* other stuff... */ |
b1234a5c |
18 | OSStatus error; |
27a3458f |
19 | EndpointRef ep; |
20 | Plug plug; |
21 | void *private_ptr; |
22 | bufchain output_data; |
23 | int connected; |
24 | int writable; |
25 | int frozen; /* this causes readability notifications to be ignored */ |
26 | int frozen_readable; /* this means we missed at least one readability |
27 | * notification while we were frozen */ |
28 | int localhost_only; /* for listening sockets */ |
29 | char oobdata[1]; |
30 | int sending_oob; |
31 | int oobpending; /* is there OOB data available to read?*/ |
32 | int oobinline; |
33 | int pending_error; /* in case send() returns error */ |
34 | int listener; |
35 | struct Socket_tag *next; |
36 | struct Socket_tag **prev; |
37 | }; |
38 | |
39 | typedef struct Socket_tag *Actual_Socket; |
40 | |
41 | struct SockAddr_tag { |
6f81f628 |
42 | int resolved; |
b1234a5c |
43 | OSStatus error; |
6f81f628 |
44 | InetHostInfo hostinfo; |
45 | char hostname[512]; |
27a3458f |
46 | }; |
47 | |
48 | /* Globals */ |
49 | |
50 | static struct { |
51 | Actual_Socket socklist; |
6f81f628 |
52 | InetSvcRef inetsvc; |
27a3458f |
53 | } ot; |
54 | |
55 | OSErr ot_init(void) |
56 | { |
6f81f628 |
57 | OSStatus err; |
58 | |
59 | err = InitOpenTransport(); |
60 | if (err != kOTNoError) return err; |
61 | ot.inetsvc = OTOpenInternetServices(kDefaultInternetServicesPath, 0, &err); |
62 | return err; |
27a3458f |
63 | } |
64 | |
65 | void ot_cleanup(void) |
66 | { |
67 | Actual_Socket s; |
68 | |
69 | for (s = ot.socklist; s !=NULL; s = s->next) { |
70 | OTUnbind(s->ep); |
71 | OTCloseProvider(s->ep); |
72 | } |
73 | |
74 | CloseOpenTransport(); |
75 | } |
76 | |
2e96d504 |
77 | SockAddr ot_namelookup(char const *host, char **canonicalname) |
27a3458f |
78 | { |
79 | SockAddr ret = smalloc(sizeof(struct SockAddr_tag)); |
6f81f628 |
80 | char *realhost; |
2e96d504 |
81 | |
82 | /* Casting away const -- hope OTInetStringToAddress is sensible */ |
83 | ret->error = OTInetStringToAddress(ot.inetsvc, (char *)host, |
84 | &ret->hostinfo); |
6f81f628 |
85 | ret->resolved = TRUE; |
86 | |
87 | if (ret->error == kOTNoError) |
88 | realhost = ret->hostinfo.name; |
89 | else |
90 | realhost = ""; |
91 | *canonicalname = smalloc(1+strlen(realhost)); |
92 | strcpy(*canonicalname, realhost); |
27a3458f |
93 | return ret; |
94 | } |
95 | |
2e96d504 |
96 | SockAddr ot_nonamelookup(char const *host) |
27a3458f |
97 | { |
98 | SockAddr ret = smalloc(sizeof(struct SockAddr_tag)); |
99 | |
6f81f628 |
100 | ret->resolved = FALSE; |
101 | ret->error = kOTNoError; |
102 | ret->hostname[0] = '\0'; |
103 | strncat(ret->hostname, host, lenof(ret->hostname) - 1); |
27a3458f |
104 | return ret; |
105 | } |
106 | |
107 | void ot_getaddr(SockAddr addr, char *buf, int buflen) |
108 | { |
6f81f628 |
109 | char mybuf[16]; |
110 | |
111 | buf[0] = '\0'; |
112 | if (addr->resolved) { |
113 | /* XXX only return first address */ |
114 | OTInetHostToString(addr->hostinfo.addrs[0], mybuf); |
115 | strncat(buf, mybuf, buflen - 1); |
116 | } else |
117 | strncat(buf, addr->hostname, buflen - 1); |
27a3458f |
118 | } |
119 | |
120 | /* I think "local" here really means "loopback" */ |
121 | |
122 | int ot_hostname_is_local(char *name) |
123 | { |
124 | |
125 | return !strcmp(name, "localhost"); |
126 | } |
127 | |
128 | int ot_address_is_local(SockAddr addr) |
129 | { |
6f81f628 |
130 | int i; |
27a3458f |
131 | |
6f81f628 |
132 | if (addr->resolved) |
133 | for (i = 0; i < kMaxHostAddrs; i++) |
134 | if (addr->hostinfo.addrs[i] & 0xff000000 == 0x7f000000) |
135 | return TRUE; |
27a3458f |
136 | return FALSE; |
137 | } |
138 | |
139 | int ot_addrtype(SockAddr addr) |
140 | { |
6f81f628 |
141 | |
142 | if (addr->resolved) |
143 | return ADDRTYPE_IPV4; |
144 | return ADDRTYPE_NAME; |
27a3458f |
145 | } |
146 | |
147 | void ot_addrcopy(SockAddr addr, char *buf) |
148 | { |
6f81f628 |
149 | |
150 | /* XXX only return first address */ |
151 | memcpy(buf, &addr->hostinfo.addrs[0], 4); |
27a3458f |
152 | } |
153 | |
154 | void ot_addr_free(SockAddr addr) |
155 | { |
156 | sfree(addr); |
157 | } |
158 | |
159 | |
160 | static Plug ot_tcp_plug(Socket sock, Plug p) |
161 | { |
162 | Actual_Socket s = (Actual_Socket) sock; |
163 | Plug ret = s->plug; |
164 | if (p) |
165 | s->plug = p; |
166 | return ret; |
167 | } |
168 | |
169 | static void ot_tcp_flush(Socket s) |
170 | { |
171 | /* |
172 | * We send data to the socket as soon as we can anyway, |
173 | * so we don't need to do anything here. :-) |
174 | */ |
175 | } |
176 | |
177 | static void ot_tcp_close(Socket s); |
178 | static int ot_tcp_write(Socket s, char const *data, int len); |
179 | static int ot_tcp_write_oob(Socket s, char const *data, int len); |
180 | static void ot_tcp_set_private_ptr(Socket s, void *ptr); |
181 | static void *ot_tcp_get_private_ptr(Socket s); |
182 | static void ot_tcp_set_frozen(Socket s, int is_frozen); |
183 | static char *ot_tcp_socket_error(Socket s); |
184 | static void ot_recv(Actual_Socket s); |
185 | void ot_poll(void); |
186 | |
187 | Socket ot_register(void *sock, Plug plug) |
188 | { |
189 | static struct socket_function_table fn_table = { |
190 | ot_tcp_plug, |
191 | ot_tcp_close, |
192 | ot_tcp_write, |
193 | ot_tcp_write_oob, |
194 | ot_tcp_flush, |
195 | ot_tcp_set_private_ptr, |
196 | ot_tcp_get_private_ptr, |
197 | ot_tcp_set_frozen, |
198 | ot_tcp_socket_error |
199 | }; |
200 | |
201 | Actual_Socket ret; |
202 | |
203 | ret = smalloc(sizeof(struct Socket_tag)); |
204 | ret->fn = &fn_table; |
b1234a5c |
205 | ret->error = kOTNoError; |
27a3458f |
206 | ret->plug = plug; |
207 | bufchain_init(&ret->output_data); |
208 | ret->writable = 1; /* to start with */ |
209 | ret->sending_oob = 0; |
210 | ret->frozen = 1; |
211 | ret->frozen_readable = 0; |
212 | ret->localhost_only = 0; /* unused, but best init anyway */ |
213 | ret->pending_error = 0; |
214 | ret->oobpending = FALSE; |
215 | ret->listener = 0; |
216 | |
217 | ret->ep = (EndpointRef)sock; |
218 | |
219 | /* some sort of error checking */ |
220 | |
221 | ret->oobinline = 0; |
222 | |
223 | /* Add this to the list of all sockets */ |
224 | ret->next = ot.socklist; |
225 | ret->prev = &ot.socklist; |
226 | ot.socklist = ret; |
227 | |
228 | return (Socket) ret; |
229 | } |
230 | |
231 | Socket ot_new(SockAddr addr, int port, int privport, int oobinline, |
232 | int nodelay, Plug plug) |
233 | { |
234 | static struct socket_function_table fn_table = { |
235 | ot_tcp_plug, |
236 | ot_tcp_close, |
237 | ot_tcp_write, |
238 | ot_tcp_write_oob, |
239 | ot_tcp_flush, |
240 | ot_tcp_set_private_ptr, |
241 | ot_tcp_get_private_ptr, |
242 | ot_tcp_set_frozen, |
243 | ot_tcp_socket_error |
244 | }; |
245 | |
246 | Actual_Socket ret; |
247 | EndpointRef ep; |
248 | OSStatus err; |
6f81f628 |
249 | InetAddress dest; |
27a3458f |
250 | TCall connectCall; |
251 | |
252 | ret = smalloc(sizeof(struct Socket_tag)); |
253 | ret->fn = &fn_table; |
b1234a5c |
254 | ret->error = kOTNoError; |
27a3458f |
255 | ret->plug = plug; |
256 | bufchain_init(&ret->output_data); |
257 | ret->connected = 0; /* to start with */ |
258 | ret->writable = 0; /* to start with */ |
259 | ret->sending_oob = 0; |
260 | ret->frozen = 0; |
261 | ret->frozen_readable = 0; |
262 | ret->localhost_only = 0; /* unused, but best init anyway */ |
263 | ret->pending_error = 0; |
264 | ret->oobinline = oobinline; |
265 | ret->oobpending = FALSE; |
266 | ret->listener = 0; |
267 | |
268 | /* Open Endpoint, configure it for TCP over anything */ |
269 | |
270 | ep = OTOpenEndpoint(OTCreateConfiguration("tcp"), 0, NULL, &err); |
271 | |
272 | ret->ep = ep; |
273 | |
274 | if (err) { |
b1234a5c |
275 | ret->error = err; |
27a3458f |
276 | return (Socket) ret; |
277 | } |
278 | |
279 | /* TODO: oobinline, nodelay */ |
280 | |
281 | /* |
282 | * Bind to local address. |
283 | */ |
284 | |
285 | /* FIXME: pay attention to privport */ |
286 | |
287 | err = OTBind(ep, NULL, NULL); /* OpenTransport always picks our address */ |
288 | |
289 | if (err) { |
b1234a5c |
290 | ret->error = err; |
27a3458f |
291 | return (Socket) ret; |
292 | } |
293 | |
294 | /* |
295 | * Connect to remote address. |
296 | */ |
297 | |
6f81f628 |
298 | /* XXX Try non-primary addresses */ |
299 | OTInitInetAddress(&dest, port, addr->hostinfo.addrs[0]); |
27a3458f |
300 | |
40d38714 |
301 | memset(&connectCall, 0, sizeof(TCall)); |
6f81f628 |
302 | connectCall.addr.buf = (UInt8 *) &dest; |
303 | connectCall.addr.len = sizeof(dest); |
27a3458f |
304 | |
305 | err = OTConnect(ep, &connectCall, nil); |
306 | |
307 | if (err) { |
b1234a5c |
308 | ret->error = err; |
27a3458f |
309 | return (Socket) ret; |
310 | } else { |
311 | ret->connected = 1; |
312 | ret->writable = 1; |
313 | } |
314 | |
315 | /* Add this to the list of all sockets */ |
316 | ret->next = ot.socklist; |
317 | ret->prev = &ot.socklist; |
b89053c0 |
318 | if (ret->next != NULL) |
319 | ret->next->prev = &ret->next; |
27a3458f |
320 | ot.socklist = ret; |
321 | |
322 | return (Socket) ret; |
323 | } |
324 | |
325 | Socket ot_newlistener(char *foobar, int port, Plug plug, int local_host_only) |
326 | { |
327 | Actual_Socket s; |
328 | |
329 | return (Socket) s; |
330 | } |
331 | |
332 | static void ot_tcp_close(Socket sock) |
333 | { |
334 | Actual_Socket s = (Actual_Socket) sock; |
335 | |
336 | OTCloseProvider(s->ep); |
337 | |
338 | /* Unhitch from list of sockets */ |
339 | *s->prev = s->next; |
340 | if (s->next != NULL) |
341 | s->next->prev = s->prev; |
342 | |
343 | sfree(s); |
344 | } |
345 | |
346 | static void try_send(Actual_Socket s) |
347 | { |
348 | while (bufchain_size(&s->output_data) > 0) { |
349 | int nsent; |
350 | void *data; |
351 | int len; |
352 | |
353 | /* Don't care about oob right now */ |
354 | |
355 | bufchain_prefix(&s->output_data, &data, &len); |
356 | |
357 | nsent = OTSnd(s->ep, data, len, 0); |
358 | noise_ultralight(nsent); |
359 | |
360 | if (nsent <= 0) { |
361 | /* something bad happened, hey ho */ |
362 | } else { |
363 | /* still don't care about oob */ |
364 | bufchain_consume(&s->output_data, nsent); |
365 | } |
366 | } |
367 | } |
368 | |
369 | static int ot_tcp_write(Socket sock, char const *buf, int len) |
370 | { |
371 | Actual_Socket s = (Actual_Socket) sock; |
372 | |
373 | bufchain_add(&s->output_data, buf, len); |
374 | |
375 | if (s->writable) |
376 | try_send(s); |
377 | return bufchain_size(&s->output_data); |
378 | } |
379 | |
380 | static int ot_tcp_write_oob(Socket sock, char const *buf, int len) |
381 | { |
382 | /* Don't care about oob */ |
383 | return 0; |
384 | } |
385 | |
386 | |
387 | /* |
388 | * Each socket abstraction contains a `void *' private field in |
389 | * which the client can keep state. |
390 | */ |
391 | static void ot_tcp_set_private_ptr(Socket sock, void *ptr) |
392 | { |
393 | Actual_Socket s = (Actual_Socket) sock; |
394 | s->private_ptr = ptr; |
395 | } |
396 | |
397 | static void *ot_tcp_get_private_ptr(Socket sock) |
398 | { |
399 | Actual_Socket s = (Actual_Socket) sock; |
400 | return s->private_ptr; |
401 | } |
402 | |
403 | |
404 | /* |
405 | * Special error values are returned from ot_namelookup and ot_new |
406 | * if there's a problem. These functions extract an error message, |
407 | * or return NULL if there's no problem. |
408 | */ |
409 | char *ot_addr_error(SockAddr addr) |
410 | { |
b1234a5c |
411 | static char buf[128]; |
412 | |
413 | if (addr->error == kOTNoError) |
414 | return NULL; |
415 | sprintf(buf, "error %d", addr->error); |
416 | return buf; |
27a3458f |
417 | } |
418 | static char *ot_tcp_socket_error(Socket sock) |
419 | { |
420 | Actual_Socket s = (Actual_Socket) sock; |
b1234a5c |
421 | static char buf[128]; |
422 | |
423 | if (s->error == kOTNoError) |
424 | return NULL; |
425 | sprintf(buf, "error %d", s->error); |
426 | return buf; |
27a3458f |
427 | } |
428 | |
429 | static void ot_tcp_set_frozen(Socket sock, int is_frozen) |
430 | { |
431 | Actual_Socket s = (Actual_Socket) sock; |
432 | |
433 | if (s->frozen == is_frozen) |
434 | return; |
435 | s->frozen = is_frozen; |
436 | } |
437 | |
438 | /* |
439 | * Poll all our sockets from an event loop |
440 | */ |
441 | |
442 | void ot_poll(void) |
443 | { |
444 | Actual_Socket s; |
445 | OTResult o; |
446 | |
447 | for (s = ot.socklist; s != NULL; s = s->next) { |
448 | o = OTLook(s->ep); |
449 | |
450 | switch(o) { |
451 | case T_DATA: /* Normal Data */ |
452 | ot_recv(s); |
453 | break; |
454 | case T_EXDATA: /* Expedited Data (urgent?) */ |
455 | ot_recv(s); |
456 | break; |
457 | } |
458 | } |
459 | } |
460 | |
461 | void ot_recv(Actual_Socket s) |
462 | { |
463 | OTResult o; |
464 | char buf[20480]; |
465 | OTFlags flags; |
466 | |
467 | if (s->frozen) return; |
468 | |
b89053c0 |
469 | do { |
470 | o = OTRcv(s->ep, buf, sizeof(buf), &flags); |
471 | if (o > 0) |
e0c47203 |
472 | plug_receive(s->plug, 0, buf, o); |
b89053c0 |
473 | if (o < 0 && o != kOTNoDataErr) |
474 | plug_closing(s->plug, NULL, 0, 0); /* XXX Error msg */ |
475 | } while (o > 0); |
27a3458f |
476 | } |
477 | |
478 | |
479 | /* |
480 | * Local Variables: |
481 | * c-file-style: "simon" |
482 | * End: |
483 | */ |