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