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