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