]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/mtcpnet.c
Cleanups of the GSSAPI support. On Windows, standard GSS libraries
[PuTTY.git] / mac / mtcpnet.c
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
30 #if !TARGET_API_MAC_CARBON
31
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
42 #define DEFINE_PLUG_METHOD_MACROS
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
54 static OSErr OpenResolver(char *);
55 static OSErr CloseResolver(void);
56
57 enum {
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
70 typedef struct hostInfo {
71     int rtnCode;
72     char cname[255];
73     unsigned long addr[NUM_ALT_ADDRS];
74 };
75
76 typedef CALLBACK_API(void, ResultProcPtr)(struct hostInfo *, char *);
77 typedef STACK_UPP_TYPE(ResultProcPtr) ResultUPP;
78 enum { 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
88 static OSErr StrToAddr(char *, struct hostInfo *, ResultUPP *, char *);
89
90 typedef CALLBACK_API_C(OSErr, OpenResolverProcPtr)(UInt32, char *);
91 typedef STACK_UPP_TYPE(OpenResolverProcPtr) OpenResolverUPP;
92 enum { 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
101 typedef CALLBACK_API_C(OSErr, CloseResolverProcPtr)(UInt32);
102 typedef STACK_UPP_TYPE(CloseResolverProcPtr) CloseResolverUPP;
103 enum { 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
110 typedef CALLBACK_API_C(OSErr, StrToAddrProcPtr)(UInt32, char *,
111                                                 struct hostInfo *, ResultUPP,
112                                                 char *);
113 typedef STACK_UPP_TYPE(StrToAddrProcPtr) StrToAddrUPP;
114 enum { 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
131 typedef CALLBACK_API_C(OSErr, AddrToStrProcPtr)(UInt32, unsigned long, char *);
132 typedef STACK_UPP_TYPE(AddrToStrProcPtr) AddrToStrUPP;
133 enum { uppAddrToStrProcInfo = kCStackBased
134        | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
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 *)))
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
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
161 struct Socket_tag {
162     struct socket_function_table *fn;
163     /* the above variable absolutely *must* be the first in this structure */
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;
181     struct Socket_tag *next;
182     struct Socket_tag **prev;
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  */
193 typedef struct Socket_tag *Actual_Socket;
194
195 struct SockAddr_tag {
196     int resolved;
197     struct hostInfo hostinfo;
198     char hostname[512];
199 };
200
201 /* Global variables */
202 static struct {
203     Handle dnr_handle;
204     int initialised;
205     short refnum;
206     ProcessSerialNumber self;
207     Actual_Socket socklist;
208 } mactcp;
209
210 static pascal void mactcp_lookupdone(struct hostInfo *hi, char *cookie);
211 static pascal void mactcp_asr(StreamPtr, unsigned short, Ptr, unsigned short,
212                               struct ICMPReport *);
213 static Plug mactcp_plug(Socket, Plug);
214 static void mactcp_flush(Socket);
215 static void mactcp_close(Socket);
216 static int mactcp_write(Socket, char const *, int);
217 static int mactcp_write_oob(Socket, char const*, int);
218 static void mactcp_set_private_ptr(Socket, void *);
219 static void *mactcp_get_private_ptr(Socket);
220 static const char *mactcp_socket_error(Socket);
221 static void mactcp_set_frozen(Socket, int);
222
223 static void mactcp_recv(Actual_Socket s, size_t len);
224
225 /*
226  * Initialise MacTCP.
227  * This should be called once before any TCP connection is opened.
228  */
229
230 OSErr 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,
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)
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
250 void mactcp_cleanup(void)
251 {
252     Actual_Socket s, next;
253
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      */
270     CloseResolver();
271     mactcp.initialised = FALSE;
272 }
273
274 static ResultUPP mactcp_lookupdone_upp;
275
276 SockAddr mactcp_namelookup(char const *host, char **canonicalname)
277 {
278     SockAddr ret = snew(struct SockAddr_tag);
279     OSErr err;
280     volatile int done = FALSE;
281     char *realhost;
282     int realhostlen;
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);
288     /* Casting away const -- hope StrToAddr is sensible */
289     err = StrToAddr((char *)host, &ret->hostinfo, mactcp_lookupdone_upp,
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     
300     if (ret->hostinfo.rtnCode == noErr) {
301         realhost = ret->hostinfo.cname;
302         /* MacTCP puts trailing dots on canonical names. */
303         realhostlen = strlen(realhost);
304         if (realhost[realhostlen - 1] == '.')
305             realhost[realhostlen - 1] = '\0';
306     } else
307         realhost = "";
308     *canonicalname = snewn(1 + strlen(realhost), char);
309     strcpy(*canonicalname, realhost);
310     return ret;
311 }
312
313 static pascal void mactcp_lookupdone(struct hostInfo *hi, char *cookie)
314 {
315     volatile int *donep = (int *)cookie;
316
317     *donep = TRUE;
318 }
319
320 SockAddr mactcp_nonamelookup(char const *host)
321 {
322     SockAddr ret = snew(struct SockAddr_tag);
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
331 void mactcp_getaddr(SockAddr addr, char *buf, int buflen)
332 {
333     char mybuf[16];
334     OSErr err;
335
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     }
346 }
347
348 /* I think "local" here really means "loopback" */
349
350 int mactcp_hostname_is_local(char *name)
351 {
352
353     return !strcmp(name, "localhost");
354 }
355
356 int mactcp_address_is_local(SockAddr addr)
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
367 int mactcp_addrtype(SockAddr addr)
368 {
369
370     if (addr->resolved)
371         return ADDRTYPE_IPV4;
372     return ADDRTYPE_NAME;
373 }
374
375 void mactcp_addrcopy(SockAddr addr, char *buf)
376 {
377
378     /* XXX only return first address */
379     memcpy(buf, &addr->hostinfo.addr[0], 4);
380 }
381
382 void mactcp_addr_free(SockAddr addr)
383 {
384
385     sfree(addr);
386 }
387
388 static 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
398 static void mactcp_flush(Socket s)
399 {
400
401     fatalbox("mactcp_flush");
402 }
403
404 Socket mactcp_register(void *sock, Plug plug)
405 {
406
407     fatalbox("mactcp_register");
408 }
409
410 static TCPNotifyUPP mactcp_asr_upp;
411
412 Socket mactcp_new(SockAddr addr, int port, int privport, int oobinline,
413               int nodelay, int keepalive, Plug plug)
414 {
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
432     /*
433      * Create Socket structure.
434      */
435     ret = snew(struct Socket_tag);
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;
448     ret->oobinline = oobinline;
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);
467     if (ret->err != noErr) return (Socket)ret;
468
469     buflen = upb.csParam.mtu.mtuSize * 4 + 1024;
470     if (buflen < 4096) buflen = 4096;
471     if (mactcp_asr_upp == NULL)
472         mactcp_asr_upp = NewTCPNotifyUPP(&mactcp_asr);
473     GetCurrentProcess(&mactcp.self);
474     pb.ioCRefNum = mactcp.refnum;
475     pb.csCode = TCPCreate;
476     pb.csParam.create.rcvBuff = snewn(buflen, char);
477     pb.csParam.create.rcvBuffLen = buflen;
478     pb.csParam.create.notifyProc = mactcp_asr_upp;
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;
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
513     /* Add this to the list of all sockets */
514     ret->next = mactcp.socklist;
515     ret->prev = &mactcp.socklist;
516     if (ret->next != NULL)
517         ret->next->prev = &ret->next;
518     mactcp.socklist = ret;
519
520     sk_addr_free(addr); /* don't need this anymore */
521
522     return (Socket)ret;
523 }
524
525 Socket mactcp_newlistener(char *srcaddr, int port, Plug plug,
526                           int local_host_only, int address_family)
527 {
528
529     fatalbox("mactcp_newlistener");
530 }
531
532 static 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);
565
566     /* Unhitch from list of sockets */
567     *s->prev = s->next;
568     if (s->next != NULL)
569         s->next->prev = s->prev;
570
571     sfree(s);
572 }
573
574 static int mactcp_write(Socket sock, char const *buf, int len)
575 {
576     Actual_Socket s = (Actual_Socket) sock;
577     wdsEntry wds[2];
578     TCPiopb pb;
579
580     /* 
581      * Casting away const from buf should be safe -- MacTCP won't
582      * write to it.
583      */
584     wds[0].length = len;
585     wds[0].ptr = (char *)buf;
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
600 static int mactcp_write_oob(Socket sock, char const *buf, int len)
601 {
602
603     fatalbox("mactcp_write_oob");
604 }
605
606 static 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                               
614 /*
615  * Called from our event loop if there's work to do.
616  */
617 void mactcp_poll(void)
618 {
619     Actual_Socket s, next;
620     TCPiopb pb;
621
622     for (s = mactcp.socklist; s != NULL; s = next) {
623         next = s->next;
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;
634             mactcp_recv(s, pb.csParam.status.amtUnreadData);
635         } while (TRUE);
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         }
641       next_socket:
642         ;
643     }
644 }
645
646 static 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 /*
674  * Each socket abstraction contains a `void *' private field in
675  * which the client can keep state.
676  */
677 static void mactcp_set_private_ptr(Socket sock, void *ptr)
678 {
679     Actual_Socket s = (Actual_Socket) sock;
680     s->private_ptr = ptr;
681 }
682
683 static void *mactcp_get_private_ptr(Socket sock)
684 {
685     Actual_Socket s = (Actual_Socket) sock;
686     return s->private_ptr;
687 }
688
689 /*
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.
693  */
694 char *mactcp_addr_error(SockAddr addr)
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
719 static const char *mactcp_socket_error(Socket sock)
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
739 static void mactcp_set_frozen(Socket sock, int is_frozen)
740 {
741     Actual_Socket s = (Actual_Socket) sock;
742
743     if (s->frozen == is_frozen)
744         return;
745     s->frozen = is_frozen;
746 }
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
754 static 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
782     while (PBHGetFInfoSync(&pb) == noErr) {
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
817 OSErr 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
836 #endif
837
838 /*
839  * Local Variables:
840  * c-file-style: "simon"
841  * End:
842  */