Add a configuration option for TCP keepalives (SO_KEEPALIVE), default off.
[u/mdw/putty] / mac / mtcpnet.c
CommitLineData
193194e2 1/*
2 * Copyright (c) 2003 Ben Harris
3 * All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following
12 * conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
22 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26/*
27 * mtcpnet.c - MacTCP interface
28 */
29
b2859f7a 30#if !TARGET_API_MAC_CARBON
31
193194e2 32#include <MacTypes.h>
33#include <Devices.h>
34#include <Endian.h>
35#include <Folders.h>
36#include <MacTCP.h>
37#include <MixedMode.h>
38#include <Resources.h>
39
40#include <assert.h>
41
d25f9823 42#define DEFINE_PLUG_METHOD_MACROS
193194e2 43#include "putty.h"
44#include "network.h"
45#include "mac.h"
46
47/*
48 * The following structures are documented as being in
49 * <AddressXlation.h>, but that isn't shipped with Universal
50 * Interfaces, and it's easier to define them here than to require
51 * people to download yet another SDK.
52 */
53
54static OSErr OpenResolver(char *);
55static OSErr CloseResolver(void);
56
57enum {
58 OPENRESOLVER = 1,
59 CLOSERESOLVER,
60 STRTOADDR,
61 ADDRTOSTR,
62 ENUMCACHE,
63 ADDRTONAME,
64 HXINFO,
65 MXINFO
66};
67
68#define NUM_ALT_ADDRS 4
69
70typedef struct hostInfo {
71 int rtnCode;
72 char cname[255];
73 unsigned long addr[NUM_ALT_ADDRS];
74};
75
76typedef CALLBACK_API(void, ResultProcPtr)(struct hostInfo *, char *);
77typedef STACK_UPP_TYPE(ResultProcPtr) ResultUPP;
78enum { uppResultProcInfo = kPascalStackBased
79 | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(struct hostInfo*)))
80 | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char *)))
81};
82#define NewResultUPP(userRoutine) \
83 (ResultUPP)NewRoutineDescriptor((ProcPtr)(userRoutine), \
84 uppResultProcInfo, \
85 GetCurrentArchitecture())
86#define DisposeResultUPP(userUPP) DisposeRoutineDescriptor(userUPP)
87
88static OSErr StrToAddr(char *, struct hostInfo *, ResultUPP *, char *);
89
90typedef CALLBACK_API_C(OSErr, OpenResolverProcPtr)(UInt32, char *);
91typedef STACK_UPP_TYPE(OpenResolverProcPtr) OpenResolverUPP;
92enum { uppOpenResolverProcInfo = kCStackBased
93 | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
94 | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UInt32)))
95 | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char*)))
96};
97#define InvokeOpenResolverUPP(selector, fileName, userUPP) \
98 CALL_TWO_PARAMETER_UPP((userUPP), uppOpenResolverProcInfo, \
99 (selector), (fileName))
100
101typedef CALLBACK_API_C(OSErr, CloseResolverProcPtr)(UInt32);
102typedef STACK_UPP_TYPE(CloseResolverProcPtr) CloseResolverUPP;
103enum { uppCloseResolverProcInfo = kCStackBased
104 | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
105 | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UInt32)))
106};
107#define InvokeCloseResolverUPP(selector, userUPP) \
108 CALL_ONE_PARAMETER_UPP((userUPP), uppCloseResolverProcInfo, (selector))
109
110typedef CALLBACK_API_C(OSErr, StrToAddrProcPtr)(UInt32, char *,
111 struct hostInfo *, ResultUPP,
112 char *);
113typedef STACK_UPP_TYPE(StrToAddrProcPtr) StrToAddrUPP;
114enum { uppStrToAddrProcInfo = kCStackBased
115 | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
116 | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UInt32)))
117 | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char *)))
118 | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(struct hostInfo *)))
119 | STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(ResultUPP)))
120 | STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(char *)))
121};
122#define InvokeStrToAddrUPP(selector, hostName, hostInfoPtr, ResultProc, \
123 userDataPtr, userUPP) \
124 CALL_FIVE_PARAMETER_UPP((userUPP), uppStrToAddrProcInfo, (selector),\
125 (hostName), (hostInfoPtr), (ResultProc), \
126 (userDataPtr))
127#define StrToAddr(hostName, hostInfoPtr, ResultProc, userDataPtr) \
128 InvokeStrToAddrUPP(STRTOADDR, hostName, hostInfoPtr, ResultProc, \
129 userDataPtr, (StrToAddrUPP)*mactcp.dnr_handle)
130
131typedef CALLBACK_API_C(OSErr, AddrToStrProcPtr)(UInt32, unsigned long, char *);
132typedef STACK_UPP_TYPE(AddrToStrProcPtr) AddrToStrUPP;
133enum { uppAddrToStrProcInfo = kCStackBased
134 | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
1d81a7b6 135 | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UInt32)))
136 | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(unsigned long)))
137 | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(char *)))
193194e2 138};
139#define InvokeAddrToStrUPP(selector, addr, addrStr, userUPP) \
140 CALL_THREE_PARAMETER_UPP((userUPP), uppAddrToStrProcInfo, (selector),\
141 (addr), (addrStr))
142#define AddrToStr(addr, addrStr) \
143 InvokeAddrToStrUPP(ADDRTOSTR, addr, addrStr, \
144 (AddrToStrUPP)*mactcp.dnr_handle)
145
146/* End of AddressXlation.h bits */
147
77642124 148/* TCP connection states, mysteriously missing from <MacTCP.h> */
149#define TCPS_CLOSED 0
150#define TCPS_LISTEN 2
151#define TCPS_SYN_RECEIVED 4
152#define TCPS_SYN_SENT 6
153#define TCPS_ESTABLISHED 8
154#define TCPS_FIN_WAIT_1 10
155#define TCPS_FIN_WAIT_2 12
156#define TCPS_CLOSE_WAIT 14
157#define TCPS_CLOSING 16
158#define TCPS_LAST_ACK 18
159#define TCPS_TIME_WAIT 20
160
193194e2 161struct Socket_tag {
162 struct socket_function_table *fn;
163 /* the above variable absolutely *must* be the first in this structure */
2beb0fb0 164 StreamPtr s;
165 OSErr err;
166 Plug plug;
167 void *private_ptr;
168 bufchain output_data;
169 int connected;
170 int writable;
171 int frozen; /* this causes readability notifications to be ignored */
172 int frozen_readable; /* this means we missed at least one readability
173 * notification while we were frozen */
174 int localhost_only; /* for listening sockets */
175 char oobdata[1];
176 int sending_oob;
177 int oobpending; /* is there OOB data available to read? */
178 int oobinline;
179 int pending_error; /* in case send() returns error */
180 int listener;
d25f9823 181 struct Socket_tag *next;
182 struct Socket_tag **prev;
193194e2 183};
184
185/*
186 * We used to typedef struct Socket_tag *Socket.
187 *
188 * Since we have made the networking abstraction slightly more
189 * abstract, Socket no longer means a tcp socket (it could mean
190 * an ssl socket). So now we must use Actual_Socket when we know
191 * we are talking about a tcp socket.
192 */
193typedef struct Socket_tag *Actual_Socket;
194
195struct SockAddr_tag {
196 int resolved;
197 struct hostInfo hostinfo;
198 char hostname[512];
199};
200
201/* Global variables */
202static struct {
203 Handle dnr_handle;
204 int initialised;
205 short refnum;
5221f554 206 ProcessSerialNumber self;
d25f9823 207 Actual_Socket socklist;
193194e2 208} mactcp;
209
210static pascal void mactcp_lookupdone(struct hostInfo *hi, char *cookie);
5221f554 211static pascal void mactcp_asr(StreamPtr, unsigned short, Ptr, unsigned short,
212 struct ICMPReport *);
2beb0fb0 213static Plug mactcp_plug(Socket, Plug);
214static void mactcp_flush(Socket);
215static void mactcp_close(Socket);
9b57f6ea 216static int mactcp_write(Socket, char const *, int);
217static int mactcp_write_oob(Socket, char const*, int);
2beb0fb0 218static void mactcp_set_private_ptr(Socket, void *);
219static void *mactcp_get_private_ptr(Socket);
cbe2d68f 220static const char *mactcp_socket_error(Socket);
2beb0fb0 221static void mactcp_set_frozen(Socket, int);
193194e2 222
d25f9823 223static void mactcp_recv(Actual_Socket s, size_t len);
224
193194e2 225/*
226 * Initialise MacTCP.
227 * This should be called once before any TCP connection is opened.
228 */
229
230OSErr mactcp_init(void)
231{
232 OSErr err;
233
234 /*
235 * IM:Devices describes a convoluted way of finding a spare unit
236 * number to open a driver on before calling OpenDriver. Happily,
2beb0fb0 237 * the MacTCP INIT ensures that .IPP is already open (and hence
238 * has a valid unit number already) so we don't need to go through
239 * all that. (MacTCP Programmer's Guide p6)
193194e2 240 */
241 err = OpenDriver("\p.IPP", &mactcp.refnum);
242 if (err != noErr) return err;
243 err = OpenResolver(NULL);
244 if (err != noErr) return err;
245
246 mactcp.initialised = TRUE;
247 return noErr;
248}
249
27a3458f 250void mactcp_cleanup(void)
193194e2 251{
90588fc6 252 Actual_Socket s, next;
193194e2 253
90588fc6 254 /*
255 * Eventually, PuTTY should close down each session as it exits,
256 * so there should be no sockets left when we get here. Still,
257 * better safe than sorry.
258 *
259 * XXX What about in-flight aync I/O (when we support that)?
260 */
261 for (s = mactcp.socklist; s != NULL; s = next) {
262 next = s->next; /* s is about to vanish */
263 mactcp_close(&s->fn);
264 }
265
266 /*
267 * When we get async DNS, we have to wait for any outstanding
268 * requests to complete here before exiting.
269 */
193194e2 270 CloseResolver();
271 mactcp.initialised = FALSE;
272}
273
274static ResultUPP mactcp_lookupdone_upp;
275
2e96d504 276SockAddr mactcp_namelookup(char const *host, char **canonicalname)
193194e2 277{
34773273 278 SockAddr ret = snew(struct SockAddr_tag);
193194e2 279 OSErr err;
280 volatile int done = FALSE;
281 char *realhost;
faf57628 282 int realhostlen;
193194e2 283
284 /* Clear the structure. */
285 memset(ret, 0, sizeof(struct SockAddr_tag));
286 if (mactcp_lookupdone_upp == NULL)
287 mactcp_lookupdone_upp = NewResultUPP(&mactcp_lookupdone);
2e96d504 288 /* Casting away const -- hope StrToAddr is sensible */
289 err = StrToAddr((char *)host, &ret->hostinfo, mactcp_lookupdone_upp,
193194e2 290 (char *)&done);
291 /*
292 * PuTTY expects DNS lookups to be synchronous (see bug
293 * "async-dns"), so we pretend they are.
294 */
295 if (err == cacheFault)
296 while (!done)
297 continue;
298 ret->resolved = TRUE;
299
faf57628 300 if (ret->hostinfo.rtnCode == noErr) {
193194e2 301 realhost = ret->hostinfo.cname;
faf57628 302 /* MacTCP puts trailing dots on canonical names. */
303 realhostlen = strlen(realhost);
304 if (realhost[realhostlen - 1] == '.')
305 realhost[realhostlen - 1] = '\0';
306 } else
193194e2 307 realhost = "";
34773273 308 *canonicalname = snewn(1 + strlen(realhost), char);
193194e2 309 strcpy(*canonicalname, realhost);
310 return ret;
311}
312
313static pascal void mactcp_lookupdone(struct hostInfo *hi, char *cookie)
314{
315 volatile int *donep = (int *)cookie;
316
317 *donep = TRUE;
318}
319
2e96d504 320SockAddr mactcp_nonamelookup(char const *host)
193194e2 321{
34773273 322 SockAddr ret = snew(struct SockAddr_tag);
193194e2 323
324 ret->resolved = FALSE;
325 ret->hostinfo.rtnCode = noErr;
326 ret->hostname[0] = '\0';
327 strncat(ret->hostname, host, lenof(ret->hostname) - 1);
328 return ret;
329}
330
deed9e25 331void mactcp_getaddr(SockAddr addr, char *buf, int buflen)
193194e2 332{
333 char mybuf[16];
334 OSErr err;
335
d25f9823 336 if (addr->resolved) {
337 /* XXX only return first address */
338 err = AddrToStr(addr->hostinfo.addr[0], mybuf);
339 buf[0] = '\0';
340 if (err != noErr)
341 strncat(buf, mybuf, buflen - 1);
342 } else {
343 buf[0] = '\0';
344 strncat(buf, addr->hostname, buflen - 1);
345 }
193194e2 346}
347
348/* I think "local" here really means "loopback" */
349
deed9e25 350int mactcp_hostname_is_local(char *name)
193194e2 351{
352
353 return !strcmp(name, "localhost");
354}
355
deed9e25 356int mactcp_address_is_local(SockAddr addr)
193194e2 357{
358 int i;
359
360 if (addr->resolved)
361 for (i = 0; i < NUM_ALT_ADDRS; i++)
362 if (addr->hostinfo.addr[i] & 0xff000000 == 0x7f000000)
363 return TRUE;
364 return FALSE;
365}
366
deed9e25 367int mactcp_addrtype(SockAddr addr)
193194e2 368{
369
370 if (addr->resolved)
371 return ADDRTYPE_IPV4;
372 return ADDRTYPE_NAME;
373}
374
deed9e25 375void mactcp_addrcopy(SockAddr addr, char *buf)
193194e2 376{
377
378 /* XXX only return first address */
379 memcpy(buf, &addr->hostinfo.addr[0], 4);
380}
381
deed9e25 382void mactcp_addr_free(SockAddr addr)
193194e2 383{
384
385 sfree(addr);
386}
387
2beb0fb0 388static Plug mactcp_plug(Socket sock, Plug p)
389{
390 Actual_Socket s = (Actual_Socket) sock;
391 Plug ret = s->plug;
392
393 if (p)
394 s->plug = p;
395 return ret;
396}
397
398static void mactcp_flush(Socket s)
399{
400
deed9e25 401 fatalbox("mactcp_flush");
2beb0fb0 402}
403
deed9e25 404Socket mactcp_register(void *sock, Plug plug)
0c4b7799 405{
406
deed9e25 407 fatalbox("mactcp_register");
0c4b7799 408}
409
5221f554 410static TCPNotifyUPP mactcp_asr_upp;
411
deed9e25 412Socket mactcp_new(SockAddr addr, int port, int privport, int oobinline,
79bf227b 413 int nodelay, int keepalive, Plug plug)
193194e2 414{
2beb0fb0 415 static struct socket_function_table fn_table = {
416 mactcp_plug,
417 mactcp_close,
418 mactcp_write,
419 mactcp_write_oob,
420 mactcp_flush,
421 mactcp_set_private_ptr,
422 mactcp_get_private_ptr,
423 mactcp_set_frozen,
424 mactcp_socket_error
425 };
426 TCPiopb pb;
427 UDPiopb upb;
428 Actual_Socket ret;
429 ip_addr dstaddr;
430 size_t buflen;
431
2beb0fb0 432 /*
433 * Create Socket structure.
434 */
34773273 435 ret = snew(struct Socket_tag);
2beb0fb0 436 ret->s = 0;
437 ret->fn = &fn_table;
438 ret->err = noErr;
439 ret->plug = plug;
440 bufchain_init(&ret->output_data);
441 ret->connected = 0; /* to start with */
442 ret->writable = 0; /* to start with */
443 ret->sending_oob = 0;
444 ret->frozen = 0;
445 ret->frozen_readable = 0;
446 ret->localhost_only = 0; /* unused, but best init anyway */
447 ret->pending_error = 0;
d25f9823 448 ret->oobinline = oobinline;
2beb0fb0 449 ret->oobpending = FALSE;
450 ret->listener = 0;
451
452 dstaddr = addr->hostinfo.addr[0]; /* XXX should try all of them */
453 /*
454 * Create a TCP stream.
455 *
456 * MacTCP requires us to provide it with some buffer memory. Page
457 * 31 of the Programmer's Guide says it should be a minimum of
458 * 4*MTU+1024. Page 36 says a minimum of 4096 bytes. Assume
459 * they're both correct.
460 */
461 assert(addr->resolved);
462 upb.ioCRefNum = mactcp.refnum;
463 upb.csCode = UDPMaxMTUSize;
464 upb.csParam.mtu.remoteHost = dstaddr;
465 upb.csParam.mtu.userDataPtr = NULL;
466 ret->err = PBControlSync((ParmBlkPtr)&upb);
2beb0fb0 467 if (ret->err != noErr) return (Socket)ret;
2beb0fb0 468
469 buflen = upb.csParam.mtu.mtuSize * 4 + 1024;
470 if (buflen < 4096) buflen = 4096;
5221f554 471 if (mactcp_asr_upp == NULL)
472 mactcp_asr_upp = NewTCPNotifyUPP(&mactcp_asr);
473 GetCurrentProcess(&mactcp.self);
2beb0fb0 474 pb.ioCRefNum = mactcp.refnum;
475 pb.csCode = TCPCreate;
34773273 476 pb.csParam.create.rcvBuff = snewn(buflen, char);
2beb0fb0 477 pb.csParam.create.rcvBuffLen = buflen;
5221f554 478 pb.csParam.create.notifyProc = mactcp_asr_upp;
2beb0fb0 479 pb.csParam.create.userDataPtr = (Ptr)ret;
480 ret->err = PBControlSync((ParmBlkPtr)&pb);
481 if (ret->err != noErr) return (Socket)ret;
482 ret->s = pb.tcpStream;
2beb0fb0 483
484 /*
485 * Open the connection.
486 */
487 pb.ioCRefNum = mactcp.refnum;
488 pb.csCode = TCPActiveOpen;
489 pb.tcpStream = ret->s;
490 pb.csParam.open.validityFlags = 0;
491 pb.csParam.open.remoteHost = dstaddr;
492 pb.csParam.open.remotePort = port;
493 pb.csParam.open.localPort = privport ? 1023 : 0;
494 pb.csParam.open.dontFrag = FALSE;
495 pb.csParam.open.timeToLive = 0;
496 pb.csParam.open.security = 0;
497 pb.csParam.open.optionCnt = 0;
498 pb.csParam.open.userDataPtr = (Ptr)ret;
499 while (1) {
500 ret->err = PBControlSync((ParmBlkPtr)&pb);
501 if (!privport || ret->err != duplicateSocket)
502 break;
503 pb.csParam.open.localPort--;
504 if (pb.csParam.open.localPort == 0)
505 break;
506 }
507
508 if (ret->err != noErr) return (Socket)ret;
509
510 ret->connected = TRUE;
511 ret->writable = TRUE;
512
d25f9823 513 /* Add this to the list of all sockets */
514 ret->next = mactcp.socklist;
515 ret->prev = &mactcp.socklist;
b89053c0 516 if (ret->next != NULL)
517 ret->next->prev = &ret->next;
d25f9823 518 mactcp.socklist = ret;
519
f85e6f6e 520 sk_addr_free(addr); /* don't need this anymore */
521
2beb0fb0 522 return (Socket)ret;
523}
524
deed9e25 525Socket mactcp_newlistener(char *srcaddr, int port, Plug plug,
526 int local_host_only)
0c4b7799 527{
528
deed9e25 529 fatalbox("mactcp_newlistener");
0c4b7799 530}
531
2beb0fb0 532static void mactcp_close(Socket sock)
533{
534 Actual_Socket s = (Actual_Socket)sock;
535 TCPiopb pb;
536
537 /*
538 * TCPClose is equivalent to shutdown(fd, SHUT_WR), and hence
539 * leaves the Rx side open, while TCPAbort seems rather vicious,
540 * throwing away Tx data that haven't been ACKed yet. We do both
541 * in succession.
542 */
543 pb.ioCRefNum = mactcp.refnum;
544 pb.csCode = TCPClose;
545 pb.tcpStream = s->s;
546 pb.csParam.close.validityFlags = 0;
547 pb.csParam.close.userDataPtr = (Ptr)s;
548 s->err = PBControlSync((ParmBlkPtr)&pb);
549 /* Not much we can do about an error anyway. */
550
551 pb.ioCRefNum = mactcp.refnum;
552 pb.csCode = TCPAbort;
553 pb.tcpStream = s->s;
554 pb.csParam.abort.userDataPtr = (Ptr)s;
555 s->err = PBControlSync((ParmBlkPtr)&pb);
556 /* Even less we can do about an error here. */
557
558 pb.ioCRefNum = mactcp.refnum;
559 pb.csCode = TCPRelease;
560 pb.tcpStream = s->s;
561 pb.csParam.create.userDataPtr = (Ptr)s;
562 s->err = PBControlSync((ParmBlkPtr)&pb);
563 if (s->err == noErr)
564 sfree(pb.csParam.create.rcvBuff);
d25f9823 565
566 /* Unhitch from list of sockets */
567 *s->prev = s->next;
568 if (s->next != NULL)
569 s->next->prev = s->prev;
570
2beb0fb0 571 sfree(s);
572}
573
9b57f6ea 574static int mactcp_write(Socket sock, char const *buf, int len)
2beb0fb0 575{
576 Actual_Socket s = (Actual_Socket) sock;
577 wdsEntry wds[2];
578 TCPiopb pb;
579
9b57f6ea 580 /*
581 * Casting away const from buf should be safe -- MacTCP won't
582 * write to it.
583 */
2beb0fb0 584 wds[0].length = len;
9b57f6ea 585 wds[0].ptr = (char *)buf;
2beb0fb0 586 wds[1].length = 0;
587
588 pb.ioCRefNum = mactcp.refnum;
589 pb.csCode = TCPSend;
590 pb.tcpStream = s->s;
591 pb.csParam.send.validityFlags = 0;
592 pb.csParam.send.pushFlag = TRUE; /* XXX we want it to return. */
593 pb.csParam.send.urgentFlag = 0;
594 pb.csParam.send.wdsPtr = (Ptr)wds;
595 pb.csParam.send.userDataPtr = (Ptr)s;
596 s->err = PBControlSync((ParmBlkPtr)&pb);
597 return 0;
598}
599
9b57f6ea 600static int mactcp_write_oob(Socket sock, char const *buf, int len)
2beb0fb0 601{
602
603 fatalbox("mactcp_write_oob");
604}
605
5221f554 606static pascal void mactcp_asr(StreamPtr str, unsigned short event, Ptr cookie,
607 unsigned short termin_reason,
608 struct ICMPReport *icmp)
609{
610
611 WakeUpProcess(&mactcp.self);
612}
613
2beb0fb0 614/*
d25f9823 615 * Called from our event loop if there's work to do.
616 */
617void mactcp_poll(void)
618{
5320c492 619 Actual_Socket s, next;
d25f9823 620 TCPiopb pb;
621
5320c492 622 for (s = mactcp.socklist; s != NULL; s = next) {
623 next = s->next;
90588fc6 624 do {
625 pb.ioCRefNum = mactcp.refnum;
626 pb.csCode = TCPStatus;
627 pb.tcpStream = s->s;
628 pb.csParam.status.userDataPtr = (Ptr)s;
629 s->err = PBControlSync((ParmBlkPtr)&pb);
630 if (s->err != noErr)
631 goto next_socket;
632 if (pb.csParam.status.amtUnreadData == 0)
633 break;
d25f9823 634 mactcp_recv(s, pb.csParam.status.amtUnreadData);
90588fc6 635 } while (TRUE);
77642124 636 switch (pb.csParam.status.connectionState) {
637 case TCPS_CLOSE_WAIT:
638 /* Remote end has sent us a FIN */
639 plug_closing(s->plug, NULL, 0, 0);
640 }
90588fc6 641 next_socket:
642 ;
d25f9823 643 }
644}
645
646static void mactcp_recv(Actual_Socket s, size_t len)
647{
648 rdsEntry rds[2];
649 TCPiopb pb;
650
651 if (s->frozen) return;
652
653 while (len > 0) {
654 pb.ioCRefNum = mactcp.refnum;
655 pb.csCode = TCPNoCopyRcv;
656 pb.tcpStream = s->s;
657 pb.csParam.receive.commandTimeoutValue = 0;
658 pb.csParam.receive.rdsPtr = (Ptr)rds;
659 pb.csParam.receive.rdsLength = lenof(rds) - 1;
660 pb.csParam.receive.userDataPtr = (Ptr)s;
661 s->err = PBControlSync((ParmBlkPtr)&pb);
662 if (s->err != noErr)
663 return;
664 plug_receive(s->plug, 0, rds[0].ptr, rds[0].length);
665 len -= rds[0].length;
666 pb.csCode = TCPRcvBfrReturn;
667 s->err = PBControlSync((ParmBlkPtr)&pb);
668 if (s->err != noErr)
669 return;
670 }
671}
672
673/*
2beb0fb0 674 * Each socket abstraction contains a `void *' private field in
675 * which the client can keep state.
676 */
677static void mactcp_set_private_ptr(Socket sock, void *ptr)
678{
679 Actual_Socket s = (Actual_Socket) sock;
680 s->private_ptr = ptr;
681}
193194e2 682
2beb0fb0 683static void *mactcp_get_private_ptr(Socket sock)
684{
685 Actual_Socket s = (Actual_Socket) sock;
686 return s->private_ptr;
193194e2 687}
688
689/*
deed9e25 690 * Special error values are returned from mactcp_namelookup and
691 * mactcp_new if there's a problem. These functions extract an error
692 * message, or return NULL if there's no problem.
193194e2 693 */
deed9e25 694char *mactcp_addr_error(SockAddr addr)
193194e2 695{
696 static char buf[64];
697
698 switch (addr->hostinfo.rtnCode) {
699 case noErr:
700 return NULL;
701 case nameSyntaxErr:
702 return "Name syntax error";
703 case noNameServer:
704 return "No name server found";
705 case authNameErr:
706 return "Domain name does not exist";
707 case noAnsErr:
708 return "No answer from domain name server";
709 case dnrErr:
710 return "Domain name server returned an error";
711 case outOfMemory:
712 return "Out of memory";
713 default:
714 sprintf(buf, "Unknown DNR error %d", addr->hostinfo.rtnCode);
715 return buf;
716 }
717}
718
cbe2d68f 719static const char *mactcp_socket_error(Socket sock)
2beb0fb0 720{
721 static char buf[64];
722 Actual_Socket s = (Actual_Socket) sock;
723
724 switch (s->err) {
725 case noErr:
726 return NULL;
727 case insufficientResources:
728 return "Insufficient resources to open TCP stream";
729 case duplicateSocket:
730 return "Duplicate socket";
731 case openFailed:
732 return "Connection failed while opening";
733 default:
734 sprintf(buf, "Unknown MacTCP error %d", s->err);
735 return buf;
736 }
737}
738
739static void mactcp_set_frozen(Socket sock, int is_frozen)
740{
d25f9823 741 Actual_Socket s = (Actual_Socket) sock;
2beb0fb0 742
d25f9823 743 if (s->frozen == is_frozen)
744 return;
745 s->frozen = is_frozen;
2beb0fb0 746}
193194e2 747
748/*
749 * Bits below here would usually be in dnr.c, shipped with the MacTCP
750 * SDK, but its convenient not to require that, and since we assume
751 * System 7 we can actually simplify things a lot.
752 */
753
754static OSErr OpenResolver(char *hosts_file)
755{
756 short vrefnum;
757 long dirid;
758 HParamBlockRec pb;
759 Str255 filename;
760 OSErr err;
761 int fd;
762 Handle dnr_handle;
763
764 if (mactcp.dnr_handle != NULL)
765 return noErr;
766
767 err = FindFolder(kOnSystemDisk, kControlPanelFolderType, FALSE, &vrefnum,
768 &dirid);
769 if (err != noErr) return err;
770
771 /*
772 * Might be better to use PBCatSearch here, but it's not always
773 * available.
774 */
775 pb.fileParam.ioCompletion = NULL;
776 pb.fileParam.ioNamePtr = filename;
777 pb.fileParam.ioVRefNum = vrefnum;
778 pb.fileParam.ioFDirIndex = 1;
779 pb.fileParam.ioDirID = dirid;
780 fd = -1;
781
2beb0fb0 782 while (PBHGetFInfoSync(&pb) == noErr) {
193194e2 783 if (pb.fileParam.ioFlFndrInfo.fdType == 'cdev' &&
784 pb.fileParam.ioFlFndrInfo.fdCreator == 'ztcp') {
785 fd = HOpenResFile(vrefnum, dirid, filename, fsRdPerm);
786 if (fd == -1) continue;
787 dnr_handle = Get1IndResource('dnrp', 1);
788 if (dnr_handle != NULL)
789 break;
790 CloseResFile(fd);
791 fd = -1;
792 }
793 pb.fileParam.ioDirID = dirid;
794 pb.fileParam.ioFDirIndex++;
795 }
796 if (fd == -1)
797 return fnfErr;
798
799 DetachResource(dnr_handle);
800 CloseResFile(fd);
801
802 MoveHHi(dnr_handle);
803 HLock(dnr_handle);
804
805 err = InvokeOpenResolverUPP(OPENRESOLVER, hosts_file,
806 (OpenResolverUPP)*dnr_handle);
807 if (err != noErr) {
808 HUnlock(dnr_handle);
809 DisposeHandle(dnr_handle);
810 return err;
811 }
812
813 mactcp.dnr_handle = dnr_handle;
814 return noErr;
815}
816
817OSErr CloseResolver(void)
818{
819 Handle dnr_handle = mactcp.dnr_handle;
820 OSErr err;
821
822 if (mactcp.dnr_handle == NULL)
823 return notOpenErr;
824
825 err = InvokeCloseResolverUPP(CLOSERESOLVER,
826 (CloseResolverUPP)*mactcp.dnr_handle);
827 if (err != noErr)
828 return err;
829
830 mactcp.dnr_handle = NULL;
831 HUnlock(dnr_handle);
832 DisposeHandle(dnr_handle);
833 return noErr;
834}
835
b2859f7a 836#endif
0c4b7799 837
193194e2 838/*
839 * Local Variables:
840 * c-file-style: "simon"
841 * End:
842 */