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; |
316 | ot.socklist = ret; |
317 | |
318 | return (Socket) ret; |
319 | } |
320 | |
321 | Socket ot_newlistener(char *foobar, int port, Plug plug, int local_host_only) |
322 | { |
323 | Actual_Socket s; |
324 | |
325 | return (Socket) s; |
326 | } |
327 | |
328 | static void ot_tcp_close(Socket sock) |
329 | { |
330 | Actual_Socket s = (Actual_Socket) sock; |
331 | |
332 | OTCloseProvider(s->ep); |
333 | |
334 | /* Unhitch from list of sockets */ |
335 | *s->prev = s->next; |
336 | if (s->next != NULL) |
337 | s->next->prev = s->prev; |
338 | |
339 | sfree(s); |
340 | } |
341 | |
342 | static void try_send(Actual_Socket s) |
343 | { |
344 | while (bufchain_size(&s->output_data) > 0) { |
345 | int nsent; |
346 | void *data; |
347 | int len; |
348 | |
349 | /* Don't care about oob right now */ |
350 | |
351 | bufchain_prefix(&s->output_data, &data, &len); |
352 | |
353 | nsent = OTSnd(s->ep, data, len, 0); |
354 | noise_ultralight(nsent); |
355 | |
356 | if (nsent <= 0) { |
357 | /* something bad happened, hey ho */ |
358 | } else { |
359 | /* still don't care about oob */ |
360 | bufchain_consume(&s->output_data, nsent); |
361 | } |
362 | } |
363 | } |
364 | |
365 | static int ot_tcp_write(Socket sock, char const *buf, int len) |
366 | { |
367 | Actual_Socket s = (Actual_Socket) sock; |
368 | |
369 | bufchain_add(&s->output_data, buf, len); |
370 | |
371 | if (s->writable) |
372 | try_send(s); |
373 | return bufchain_size(&s->output_data); |
374 | } |
375 | |
376 | static int ot_tcp_write_oob(Socket sock, char const *buf, int len) |
377 | { |
378 | /* Don't care about oob */ |
379 | return 0; |
380 | } |
381 | |
382 | |
383 | /* |
384 | * Each socket abstraction contains a `void *' private field in |
385 | * which the client can keep state. |
386 | */ |
387 | static void ot_tcp_set_private_ptr(Socket sock, void *ptr) |
388 | { |
389 | Actual_Socket s = (Actual_Socket) sock; |
390 | s->private_ptr = ptr; |
391 | } |
392 | |
393 | static void *ot_tcp_get_private_ptr(Socket sock) |
394 | { |
395 | Actual_Socket s = (Actual_Socket) sock; |
396 | return s->private_ptr; |
397 | } |
398 | |
399 | |
400 | /* |
401 | * Special error values are returned from ot_namelookup and ot_new |
402 | * if there's a problem. These functions extract an error message, |
403 | * or return NULL if there's no problem. |
404 | */ |
405 | char *ot_addr_error(SockAddr addr) |
406 | { |
b1234a5c |
407 | static char buf[128]; |
408 | |
409 | if (addr->error == kOTNoError) |
410 | return NULL; |
411 | sprintf(buf, "error %d", addr->error); |
412 | return buf; |
27a3458f |
413 | } |
414 | static char *ot_tcp_socket_error(Socket sock) |
415 | { |
416 | Actual_Socket s = (Actual_Socket) sock; |
b1234a5c |
417 | static char buf[128]; |
418 | |
419 | if (s->error == kOTNoError) |
420 | return NULL; |
421 | sprintf(buf, "error %d", s->error); |
422 | return buf; |
27a3458f |
423 | } |
424 | |
425 | static void ot_tcp_set_frozen(Socket sock, int is_frozen) |
426 | { |
427 | Actual_Socket s = (Actual_Socket) sock; |
428 | |
429 | if (s->frozen == is_frozen) |
430 | return; |
431 | s->frozen = is_frozen; |
432 | } |
433 | |
434 | /* |
435 | * Poll all our sockets from an event loop |
436 | */ |
437 | |
438 | void ot_poll(void) |
439 | { |
440 | Actual_Socket s; |
441 | OTResult o; |
442 | |
443 | for (s = ot.socklist; s != NULL; s = s->next) { |
444 | o = OTLook(s->ep); |
445 | |
446 | switch(o) { |
447 | case T_DATA: /* Normal Data */ |
448 | ot_recv(s); |
449 | break; |
450 | case T_EXDATA: /* Expedited Data (urgent?) */ |
451 | ot_recv(s); |
452 | break; |
453 | } |
454 | } |
455 | } |
456 | |
457 | void ot_recv(Actual_Socket s) |
458 | { |
459 | OTResult o; |
460 | char buf[20480]; |
461 | OTFlags flags; |
462 | |
463 | if (s->frozen) return; |
464 | |
465 | while ((o = OTRcv(s->ep, buf, sizeof(buf), &flags)) != kOTNoDataErr) { |
466 | plug_receive(s->plug, 0, buf, sizeof(buf)); |
467 | } |
468 | } |
469 | |
470 | |
471 | /* |
472 | * Local Variables: |
473 | * c-file-style: "simon" |
474 | * End: |
475 | */ |