]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/mtcpnet.c
Add reception support for MacTCP. Now I can log in over TELNET, just about.
[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 #include <MacTypes.h>
31 #include <Devices.h>
32 #include <Endian.h>
33 #include <Folders.h>
34 #include <MacTCP.h>
35 #include <MixedMode.h>
36 #include <Resources.h>
37
38 #include <assert.h>
39
40 #define DEFINE_PLUG_METHOD_MACROS
41 #include "putty.h"
42 #include "network.h"
43 #include "mac.h"
44
45 /*
46  * The following structures are documented as being in
47  * <AddressXlation.h>, but that isn't shipped with Universal
48  * Interfaces, and it's easier to define them here than to require
49  * people to download yet another SDK.
50  */
51
52 static OSErr OpenResolver(char *);
53 static OSErr CloseResolver(void);
54
55 enum {
56     OPENRESOLVER = 1,
57     CLOSERESOLVER,
58     STRTOADDR,
59     ADDRTOSTR,
60     ENUMCACHE,
61     ADDRTONAME,
62     HXINFO,
63     MXINFO
64 };
65
66 #define NUM_ALT_ADDRS 4
67
68 typedef struct hostInfo {
69     int rtnCode;
70     char cname[255];
71     unsigned long addr[NUM_ALT_ADDRS];
72 };
73
74 typedef CALLBACK_API(void, ResultProcPtr)(struct hostInfo *, char *);
75 typedef STACK_UPP_TYPE(ResultProcPtr) ResultUPP;
76 enum { uppResultProcInfo = kPascalStackBased
77        | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(struct hostInfo*)))
78        | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char *)))
79 };
80 #define NewResultUPP(userRoutine)                                       \
81     (ResultUPP)NewRoutineDescriptor((ProcPtr)(userRoutine),             \
82                                     uppResultProcInfo,                  \
83                                     GetCurrentArchitecture())
84 #define DisposeResultUPP(userUPP) DisposeRoutineDescriptor(userUPP)
85
86 static OSErr StrToAddr(char *, struct hostInfo *, ResultUPP *, char *);
87
88 typedef CALLBACK_API_C(OSErr, OpenResolverProcPtr)(UInt32, char *);
89 typedef STACK_UPP_TYPE(OpenResolverProcPtr) OpenResolverUPP;
90 enum { uppOpenResolverProcInfo = kCStackBased
91        | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
92        | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UInt32)))
93        | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char*)))
94 };
95 #define InvokeOpenResolverUPP(selector, fileName, userUPP)              \
96     CALL_TWO_PARAMETER_UPP((userUPP), uppOpenResolverProcInfo,          \
97                            (selector), (fileName))
98
99 typedef CALLBACK_API_C(OSErr, CloseResolverProcPtr)(UInt32);
100 typedef STACK_UPP_TYPE(CloseResolverProcPtr) CloseResolverUPP;
101 enum { uppCloseResolverProcInfo = kCStackBased
102        | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
103        | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UInt32)))
104 };
105 #define InvokeCloseResolverUPP(selector, userUPP)                       \
106     CALL_ONE_PARAMETER_UPP((userUPP), uppCloseResolverProcInfo, (selector))
107
108 typedef CALLBACK_API_C(OSErr, StrToAddrProcPtr)(UInt32, char *,
109                                                 struct hostInfo *, ResultUPP,
110                                                 char *);
111 typedef STACK_UPP_TYPE(StrToAddrProcPtr) StrToAddrUPP;
112 enum { uppStrToAddrProcInfo = kCStackBased
113        | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
114        | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UInt32)))
115        | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char *)))
116        | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(struct hostInfo *)))
117        | STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(ResultUPP)))
118        | STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(char *)))
119 };
120 #define InvokeStrToAddrUPP(selector, hostName, hostInfoPtr, ResultProc, \
121                            userDataPtr, userUPP)                        \
122     CALL_FIVE_PARAMETER_UPP((userUPP), uppStrToAddrProcInfo, (selector),\
123                             (hostName), (hostInfoPtr), (ResultProc),    \
124                             (userDataPtr))
125 #define StrToAddr(hostName, hostInfoPtr, ResultProc, userDataPtr)       \
126     InvokeStrToAddrUPP(STRTOADDR, hostName, hostInfoPtr, ResultProc,    \
127                        userDataPtr, (StrToAddrUPP)*mactcp.dnr_handle)
128
129 typedef CALLBACK_API_C(OSErr, AddrToStrProcPtr)(UInt32, unsigned long, char *);
130 typedef STACK_UPP_TYPE(AddrToStrProcPtr) AddrToStrUPP;
131 enum { uppAddrToStrProcInfo = kCStackBased
132        | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
133        | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(unsigned long)))
134        | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char *)))
135 };
136 #define InvokeAddrToStrUPP(selector, addr, addrStr, userUPP)            \
137     CALL_THREE_PARAMETER_UPP((userUPP), uppAddrToStrProcInfo, (selector),\
138                              (addr), (addrStr))
139 #define AddrToStr(addr, addrStr)                                        \
140     InvokeAddrToStrUPP(ADDRTOSTR, addr, addrStr,                        \
141                        (AddrToStrUPP)*mactcp.dnr_handle)
142
143 /* End of AddressXlation.h bits */
144
145 struct Socket_tag {
146     struct socket_function_table *fn;
147     /* the above variable absolutely *must* be the first in this structure */
148     StreamPtr s;
149     OSErr err;
150     Plug plug;
151     void *private_ptr;
152     bufchain output_data;
153     int connected;
154     int writable;
155     int frozen; /* this causes readability notifications to be ignored */
156     int frozen_readable; /* this means we missed at least one readability
157                           * notification while we were frozen */
158     int localhost_only;                /* for listening sockets */
159     char oobdata[1];
160     int sending_oob;
161     int oobpending;                    /* is there OOB data available to read? */
162     int oobinline;
163     int pending_error;                 /* in case send() returns error */
164     int listener;
165     struct Socket_tag *next;
166     struct Socket_tag **prev;
167 };
168
169 /*
170  * We used to typedef struct Socket_tag *Socket.
171  *
172  * Since we have made the networking abstraction slightly more
173  * abstract, Socket no longer means a tcp socket (it could mean
174  * an ssl socket).  So now we must use Actual_Socket when we know
175  * we are talking about a tcp socket.
176  */
177 typedef struct Socket_tag *Actual_Socket;
178
179 struct SockAddr_tag {
180     int resolved;
181     struct hostInfo hostinfo;
182     char hostname[512];
183 };
184
185 /* Global variables */
186 static struct {
187     Handle dnr_handle;
188     int initialised;
189     short refnum;
190     Actual_Socket socklist;
191 } mactcp;
192
193 static pascal void mactcp_lookupdone(struct hostInfo *hi, char *cookie);
194 static Plug mactcp_plug(Socket, Plug);
195 static void mactcp_flush(Socket);
196 static void mactcp_close(Socket);
197 static int mactcp_write(Socket, char *, int);
198 static int mactcp_write_oob(Socket, char *, int);
199 static void mactcp_set_private_ptr(Socket, void *);
200 static void *mactcp_get_private_ptr(Socket);
201 static char *mactcp_socket_error(Socket);
202 static void mactcp_set_frozen(Socket, int);
203
204 static void mactcp_recv(Actual_Socket s, size_t len);
205
206 /*
207  * Initialise MacTCP.
208  * This should be called once before any TCP connection is opened.
209  */
210
211 OSErr mactcp_init(void)
212 {
213     OSErr err;
214
215     /*
216      * IM:Devices describes a convoluted way of finding a spare unit
217      * number to open a driver on before calling OpenDriver.  Happily,
218      * the MacTCP INIT ensures that .IPP is already open (and hence
219      * has a valid unit number already) so we don't need to go through
220      * all that.  (MacTCP Programmer's Guide p6)
221      */
222     err = OpenDriver("\p.IPP", &mactcp.refnum);
223     if (err != noErr) return err;
224     err = OpenResolver(NULL);
225     if (err != noErr) return err;
226
227     mactcp.initialised = TRUE;
228     return noErr;
229 }
230
231 void mactcp_shutdown(void)
232 {
233
234     CloseResolver();
235     mactcp.initialised = FALSE;
236 }
237
238 static ResultUPP mactcp_lookupdone_upp;
239
240 SockAddr sk_namelookup(char *host, char **canonicalname)
241 {
242     SockAddr ret = smalloc(sizeof(struct SockAddr_tag));
243     OSErr err;
244     volatile int done = FALSE;
245     char *realhost;
246
247     fprintf(stderr, "Resolving %s...\n", host);
248     /* Clear the structure. */
249     memset(ret, 0, sizeof(struct SockAddr_tag));
250     if (mactcp_lookupdone_upp == NULL)
251         mactcp_lookupdone_upp = NewResultUPP(&mactcp_lookupdone);
252     err = StrToAddr(host, &ret->hostinfo, mactcp_lookupdone_upp,
253                     (char *)&done);
254     /*
255      * PuTTY expects DNS lookups to be synchronous (see bug
256      * "async-dns"), so we pretend they are.
257      */
258     if (err == cacheFault)
259         while (!done)
260             continue;
261     ret->resolved = TRUE;
262     
263     if (ret->hostinfo.rtnCode == noErr)
264         realhost = ret->hostinfo.cname;
265     else
266         realhost = "";
267     *canonicalname = smalloc(1+strlen(realhost));
268     strcpy(*canonicalname, realhost);
269     fprintf(stderr, "canonical name = %s\n", realhost);
270     return ret;
271 }
272
273 static pascal void mactcp_lookupdone(struct hostInfo *hi, char *cookie)
274 {
275     volatile int *donep = (int *)cookie;
276
277     *donep = TRUE;
278 }
279
280 SockAddr sk_nonamelookup(char *host)
281 {
282     SockAddr ret = smalloc(sizeof(struct SockAddr_tag));
283
284     ret->resolved = FALSE;
285     ret->hostinfo.rtnCode = noErr;
286     ret->hostname[0] = '\0';
287     strncat(ret->hostname, host, lenof(ret->hostname) - 1);
288     return ret;
289 }
290
291 void sk_getaddr(SockAddr addr, char *buf, int buflen)
292 {
293     char mybuf[16];
294     OSErr err;
295
296     if (addr->resolved) {
297         /* XXX only return first address */
298         err = AddrToStr(addr->hostinfo.addr[0], mybuf);
299         buf[0] = '\0';
300         if (err != noErr)
301             strncat(buf, mybuf, buflen - 1);
302     } else {
303         buf[0] = '\0';
304         strncat(buf, addr->hostname, buflen - 1);
305     }
306 }
307
308 /* I think "local" here really means "loopback" */
309
310 int sk_hostname_is_local(char *name)
311 {
312
313     return !strcmp(name, "localhost");
314 }
315
316 int sk_address_is_local(SockAddr addr)
317 {
318     int i;
319
320     if (addr->resolved)
321         for (i = 0; i < NUM_ALT_ADDRS; i++)
322             if (addr->hostinfo.addr[i] & 0xff000000 == 0x7f000000)
323                 return TRUE;
324     return FALSE;
325 }
326
327 int sk_addrtype(SockAddr addr)
328 {
329
330     if (addr->resolved)
331         return ADDRTYPE_IPV4;
332     return ADDRTYPE_NAME;
333 }
334
335 void sk_addrcopy(SockAddr addr, char *buf)
336 {
337
338     /* XXX only return first address */
339     memcpy(buf, &addr->hostinfo.addr[0], 4);
340 }
341
342 void sk_addr_free(SockAddr addr)
343 {
344
345     sfree(addr);
346 }
347
348 static Plug mactcp_plug(Socket sock, Plug p)
349 {
350     Actual_Socket s = (Actual_Socket) sock;
351     Plug ret = s->plug;
352
353     if (p)
354         s->plug = p;
355     return ret;
356 }
357
358 static void mactcp_flush(Socket s)
359 {
360
361     fatalbox("sk_tcp_flush");
362 }
363
364 Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
365               int nodelay, Plug plug)
366 {
367     static struct socket_function_table fn_table = {
368         mactcp_plug,
369         mactcp_close,
370         mactcp_write,
371         mactcp_write_oob,
372         mactcp_flush,
373         mactcp_set_private_ptr,
374         mactcp_get_private_ptr,
375         mactcp_set_frozen,
376         mactcp_socket_error
377     };
378     TCPiopb pb;
379     UDPiopb upb;
380     Actual_Socket ret;
381     ip_addr dstaddr;
382     size_t buflen;
383
384     fprintf(stderr, "Opening socket, port = %d\n", port);
385     /*
386      * Create Socket structure.
387      */
388     ret = smalloc(sizeof(struct Socket_tag));
389     ret->s = 0;
390     ret->fn = &fn_table;
391     ret->err = noErr;
392     ret->plug = plug;
393     bufchain_init(&ret->output_data);
394     ret->connected = 0;                /* to start with */
395     ret->writable = 0;                 /* to start with */
396     ret->sending_oob = 0;
397     ret->frozen = 0;
398     ret->frozen_readable = 0;
399     ret->localhost_only = 0;           /* unused, but best init anyway */
400     ret->pending_error = 0;
401     ret->oobinline = oobinline;
402     ret->oobpending = FALSE;
403     ret->listener = 0;
404
405     dstaddr = addr->hostinfo.addr[0]; /* XXX should try all of them */
406     /*
407      * Create a TCP stream.
408      * 
409      * MacTCP requires us to provide it with some buffer memory.  Page
410      * 31 of the Programmer's Guide says it should be a minimum of
411      * 4*MTU+1024.  Page 36 says a minimum of 4096 bytes.  Assume
412      * they're both correct.
413      */
414     assert(addr->resolved);
415     upb.ioCRefNum = mactcp.refnum;
416     upb.csCode = UDPMaxMTUSize;
417     upb.csParam.mtu.remoteHost = dstaddr;
418     upb.csParam.mtu.userDataPtr = NULL;
419     ret->err = PBControlSync((ParmBlkPtr)&upb);
420     fprintf(stderr, "getting mtu, err = %d\n", ret->err);
421     if (ret->err != noErr) return (Socket)ret;
422     fprintf(stderr, "Got MTU = %d\n", upb.csParam.mtu.mtuSize);
423
424     buflen = upb.csParam.mtu.mtuSize * 4 + 1024;
425     if (buflen < 4096) buflen = 4096;
426     pb.ioCRefNum = mactcp.refnum;
427     pb.csCode = TCPCreate;
428     pb.csParam.create.rcvBuff = smalloc(buflen);
429     pb.csParam.create.rcvBuffLen = buflen;
430     pb.csParam.create.notifyProc = NULL;
431     pb.csParam.create.userDataPtr = (Ptr)ret;
432     ret->err = PBControlSync((ParmBlkPtr)&pb);
433     if (ret->err != noErr) return (Socket)ret;
434     ret->s = pb.tcpStream;
435     fprintf(stderr, "stream opened\n");
436
437     /*
438      * Open the connection.
439      */
440     pb.ioCRefNum = mactcp.refnum;
441     pb.csCode = TCPActiveOpen;
442     pb.tcpStream = ret->s;
443     pb.csParam.open.validityFlags = 0;
444     pb.csParam.open.remoteHost = dstaddr;
445     pb.csParam.open.remotePort = port;
446     pb.csParam.open.localPort = privport ? 1023 : 0;
447     pb.csParam.open.dontFrag = FALSE;
448     pb.csParam.open.timeToLive = 0;
449     pb.csParam.open.security = 0;
450     pb.csParam.open.optionCnt = 0;
451     pb.csParam.open.userDataPtr = (Ptr)ret;
452     while (1) {
453         ret->err = PBControlSync((ParmBlkPtr)&pb);
454         if (!privport || ret->err != duplicateSocket)
455             break;
456         pb.csParam.open.localPort--;
457         if (pb.csParam.open.localPort == 0)
458             break;
459     }
460
461     if (ret->err != noErr) return (Socket)ret;
462
463     ret->connected = TRUE;
464     ret->writable = TRUE;
465
466     /* Add this to the list of all sockets */
467     ret->next = mactcp.socklist;
468     ret->prev = &mactcp.socklist;
469     mactcp.socklist = ret;
470
471     fprintf(stderr, "Socket connected\n");
472     return (Socket)ret;
473 }
474
475 static void mactcp_close(Socket sock)
476 {
477     Actual_Socket s = (Actual_Socket)sock;
478     TCPiopb pb;
479
480     /*
481      * TCPClose is equivalent to shutdown(fd, SHUT_WR), and hence
482      * leaves the Rx side open, while TCPAbort seems rather vicious,
483      * throwing away Tx data that haven't been ACKed yet.  We do both
484      * in succession.
485      */
486     pb.ioCRefNum = mactcp.refnum;
487     pb.csCode = TCPClose;
488     pb.tcpStream = s->s;
489     pb.csParam.close.validityFlags = 0;
490     pb.csParam.close.userDataPtr = (Ptr)s;
491     s->err = PBControlSync((ParmBlkPtr)&pb);
492     /* Not much we can do about an error anyway. */
493
494     pb.ioCRefNum = mactcp.refnum;
495     pb.csCode = TCPAbort;
496     pb.tcpStream = s->s;
497     pb.csParam.abort.userDataPtr = (Ptr)s;
498     s->err = PBControlSync((ParmBlkPtr)&pb);
499     /* Even less we can do about an error here. */
500
501     pb.ioCRefNum = mactcp.refnum;
502     pb.csCode = TCPRelease;
503     pb.tcpStream = s->s;
504     pb.csParam.create.userDataPtr = (Ptr)s;
505     s->err = PBControlSync((ParmBlkPtr)&pb);
506     if (s->err == noErr)
507         sfree(pb.csParam.create.rcvBuff);
508
509     /* Unhitch from list of sockets */
510     *s->prev = s->next;
511     if (s->next != NULL)
512         s->next->prev = s->prev;
513
514     sfree(s);
515 }
516
517 static int mactcp_write(Socket sock, char *buf, int len)
518 {
519     Actual_Socket s = (Actual_Socket) sock;
520     wdsEntry wds[2];
521     TCPiopb pb;
522
523     wds[0].length = len;
524     wds[0].ptr = buf;
525     wds[1].length = 0;
526
527     pb.ioCRefNum = mactcp.refnum;
528     pb.csCode = TCPSend;
529     pb.tcpStream = s->s;
530     pb.csParam.send.validityFlags = 0;
531     pb.csParam.send.pushFlag = TRUE; /* XXX we want it to return. */
532     pb.csParam.send.urgentFlag = 0;
533     pb.csParam.send.wdsPtr = (Ptr)wds;
534     pb.csParam.send.userDataPtr = (Ptr)s;
535     s->err = PBControlSync((ParmBlkPtr)&pb);
536     return 0;
537 }
538
539 static int mactcp_write_oob(Socket sock, char *buf, int len)
540 {
541
542     fatalbox("mactcp_write_oob");
543 }
544
545 /*
546  * Called from our event loop if there's work to do.
547  */
548 void mactcp_poll(void)
549 {
550     Actual_Socket s;
551     TCPiopb pb;
552
553     for (s = mactcp.socklist; s != NULL; s = s->next) {
554         /* XXX above can't handle sockets being deleted. */
555         pb.ioCRefNum = mactcp.refnum;
556         pb.csCode = TCPStatus;
557         pb.tcpStream = s->s;
558         pb.csParam.status.userDataPtr = (Ptr)s;
559         s->err = PBControlSync((ParmBlkPtr)&pb);
560         if (s->err != noErr)
561             continue;
562         if (pb.csParam.status.amtUnreadData > 0)
563             mactcp_recv(s, pb.csParam.status.amtUnreadData);
564         /* Should check connectionState in case remote has closed */
565     }
566 }
567
568 static void mactcp_recv(Actual_Socket s, size_t len)
569 {
570     rdsEntry rds[2];
571     TCPiopb pb;
572
573     if (s->frozen) return;
574
575     while (len > 0) {
576         pb.ioCRefNum = mactcp.refnum;
577         pb.csCode = TCPNoCopyRcv;
578         pb.tcpStream = s->s;
579         pb.csParam.receive.commandTimeoutValue = 0;
580         pb.csParam.receive.rdsPtr = (Ptr)rds;
581         pb.csParam.receive.rdsLength = lenof(rds) - 1;
582         pb.csParam.receive.userDataPtr = (Ptr)s;
583         s->err = PBControlSync((ParmBlkPtr)&pb);
584         if (s->err != noErr)
585             return;
586         plug_receive(s->plug, 0, rds[0].ptr, rds[0].length);
587         len -= rds[0].length;
588         pb.csCode = TCPRcvBfrReturn;
589         s->err = PBControlSync((ParmBlkPtr)&pb);
590         if (s->err != noErr)
591             return;
592     }   
593 }
594
595 /*
596  * Each socket abstraction contains a `void *' private field in
597  * which the client can keep state.
598  */
599 static void mactcp_set_private_ptr(Socket sock, void *ptr)
600 {
601     Actual_Socket s = (Actual_Socket) sock;
602     s->private_ptr = ptr;
603 }
604
605 static void *mactcp_get_private_ptr(Socket sock)
606 {
607     Actual_Socket s = (Actual_Socket) sock;
608     return s->private_ptr;
609 }
610
611 /*
612  * Special error values are returned from sk_namelookup and sk_new
613  * if there's a problem. These functions extract an error message,
614  * or return NULL if there's no problem.
615  */
616 char *sk_addr_error(SockAddr addr)
617 {
618     static char buf[64];
619
620     switch (addr->hostinfo.rtnCode) {
621       case noErr:
622         return NULL;
623       case nameSyntaxErr:
624         return "Name syntax error";
625       case noNameServer:
626         return "No name server found";
627       case authNameErr:
628         return "Domain name does not exist";
629       case noAnsErr:
630         return "No answer from domain name server";
631       case dnrErr:
632         return "Domain name server returned an error";
633       case outOfMemory:
634         return "Out of memory";
635       default:
636         sprintf(buf, "Unknown DNR error %d", addr->hostinfo.rtnCode);
637         return buf;
638     }
639 }
640
641 static char *mactcp_socket_error(Socket sock)
642 {
643     static char buf[64];
644     Actual_Socket s = (Actual_Socket) sock;
645
646     switch (s->err) {
647       case noErr:
648         return NULL;
649       case insufficientResources:
650         return "Insufficient resources to open TCP stream";
651       case duplicateSocket:
652         return "Duplicate socket";
653       case openFailed:
654         return "Connection failed while opening";
655       default:
656         sprintf(buf, "Unknown MacTCP error %d", s->err);
657         return buf;
658     }
659 }
660
661 static void mactcp_set_frozen(Socket sock, int is_frozen)
662 {
663     Actual_Socket s = (Actual_Socket) sock;
664
665     if (s->frozen == is_frozen)
666         return;
667     s->frozen = is_frozen;
668 }
669
670 /*
671  * Bits below here would usually be in dnr.c, shipped with the MacTCP
672  * SDK, but its convenient not to require that, and since we assume
673  * System 7 we can actually simplify things a lot.
674  */
675
676 static OSErr OpenResolver(char *hosts_file)
677 {
678     short vrefnum;
679     long dirid;
680     HParamBlockRec pb;
681     Str255 filename;
682     OSErr err;
683     int fd;
684     Handle dnr_handle;
685
686     if (mactcp.dnr_handle != NULL)
687         return noErr;
688
689     err = FindFolder(kOnSystemDisk, kControlPanelFolderType, FALSE, &vrefnum,
690                      &dirid);
691     if (err != noErr) return err;
692
693     /*
694      * Might be better to use PBCatSearch here, but it's not always
695      * available.
696      */
697     pb.fileParam.ioCompletion = NULL;
698     pb.fileParam.ioNamePtr = filename;
699     pb.fileParam.ioVRefNum = vrefnum;
700     pb.fileParam.ioFDirIndex = 1;
701     pb.fileParam.ioDirID = dirid;
702     fd = -1;
703
704     while (PBHGetFInfoSync(&pb) == noErr) {
705         if (pb.fileParam.ioFlFndrInfo.fdType == 'cdev' &&
706             pb.fileParam.ioFlFndrInfo.fdCreator == 'ztcp') {
707             fd = HOpenResFile(vrefnum, dirid, filename, fsRdPerm);
708             if (fd == -1) continue;
709             dnr_handle = Get1IndResource('dnrp', 1);
710             if (dnr_handle != NULL)
711                 break;
712             CloseResFile(fd);
713             fd = -1;
714         }
715         pb.fileParam.ioDirID = dirid;
716         pb.fileParam.ioFDirIndex++;
717     }
718     if (fd == -1)
719         return fnfErr;
720     
721     DetachResource(dnr_handle);
722     CloseResFile(fd);
723
724     MoveHHi(dnr_handle);
725     HLock(dnr_handle);
726
727     err = InvokeOpenResolverUPP(OPENRESOLVER, hosts_file,
728                                 (OpenResolverUPP)*dnr_handle);
729     if (err != noErr) {
730         HUnlock(dnr_handle);
731         DisposeHandle(dnr_handle);
732         return err;
733     }
734
735     mactcp.dnr_handle = dnr_handle;
736     return noErr;
737 }
738
739 OSErr CloseResolver(void)
740 {
741     Handle dnr_handle = mactcp.dnr_handle;
742     OSErr err;
743
744     if (mactcp.dnr_handle == NULL)
745         return notOpenErr;
746
747     err = InvokeCloseResolverUPP(CLOSERESOLVER,
748                                  (CloseResolverUPP)*mactcp.dnr_handle);
749     if (err != noErr)
750         return err;
751
752     mactcp.dnr_handle = NULL;
753     HUnlock(dnr_handle);
754     DisposeHandle(dnr_handle);
755     return noErr;
756 }
757
758 /*
759  * Local Variables:
760  * c-file-style: "simon"
761  * End:
762  */