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