]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - mac/mtcpnet.c
35f30843d8b03d0ed7ddb8fdb72ab5534586916d
[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     /* Clear the structure. */
248     memset(ret, 0, sizeof(struct SockAddr_tag));
249     if (mactcp_lookupdone_upp == NULL)
250         mactcp_lookupdone_upp = NewResultUPP(&mactcp_lookupdone);
251     err = StrToAddr(host, &ret->hostinfo, mactcp_lookupdone_upp,
252                     (char *)&done);
253     /*
254      * PuTTY expects DNS lookups to be synchronous (see bug
255      * "async-dns"), so we pretend they are.
256      */
257     if (err == cacheFault)
258         while (!done)
259             continue;
260     ret->resolved = TRUE;
261     
262     if (ret->hostinfo.rtnCode == noErr)
263         realhost = ret->hostinfo.cname;
264     else
265         realhost = "";
266     *canonicalname = smalloc(1+strlen(realhost));
267     strcpy(*canonicalname, realhost);
268     return ret;
269 }
270
271 static pascal void mactcp_lookupdone(struct hostInfo *hi, char *cookie)
272 {
273     volatile int *donep = (int *)cookie;
274
275     *donep = TRUE;
276 }
277
278 SockAddr sk_nonamelookup(char *host)
279 {
280     SockAddr ret = smalloc(sizeof(struct SockAddr_tag));
281
282     ret->resolved = FALSE;
283     ret->hostinfo.rtnCode = noErr;
284     ret->hostname[0] = '\0';
285     strncat(ret->hostname, host, lenof(ret->hostname) - 1);
286     return ret;
287 }
288
289 void sk_getaddr(SockAddr addr, char *buf, int buflen)
290 {
291     char mybuf[16];
292     OSErr err;
293
294     if (addr->resolved) {
295         /* XXX only return first address */
296         err = AddrToStr(addr->hostinfo.addr[0], mybuf);
297         buf[0] = '\0';
298         if (err != noErr)
299             strncat(buf, mybuf, buflen - 1);
300     } else {
301         buf[0] = '\0';
302         strncat(buf, addr->hostname, buflen - 1);
303     }
304 }
305
306 /* I think "local" here really means "loopback" */
307
308 int sk_hostname_is_local(char *name)
309 {
310
311     return !strcmp(name, "localhost");
312 }
313
314 int sk_address_is_local(SockAddr addr)
315 {
316     int i;
317
318     if (addr->resolved)
319         for (i = 0; i < NUM_ALT_ADDRS; i++)
320             if (addr->hostinfo.addr[i] & 0xff000000 == 0x7f000000)
321                 return TRUE;
322     return FALSE;
323 }
324
325 int sk_addrtype(SockAddr addr)
326 {
327
328     if (addr->resolved)
329         return ADDRTYPE_IPV4;
330     return ADDRTYPE_NAME;
331 }
332
333 void sk_addrcopy(SockAddr addr, char *buf)
334 {
335
336     /* XXX only return first address */
337     memcpy(buf, &addr->hostinfo.addr[0], 4);
338 }
339
340 void sk_addr_free(SockAddr addr)
341 {
342
343     sfree(addr);
344 }
345
346 static Plug mactcp_plug(Socket sock, Plug p)
347 {
348     Actual_Socket s = (Actual_Socket) sock;
349     Plug ret = s->plug;
350
351     if (p)
352         s->plug = p;
353     return ret;
354 }
355
356 static void mactcp_flush(Socket s)
357 {
358
359     fatalbox("sk_tcp_flush");
360 }
361
362 Socket sk_register(void *sock, Plug plug)
363 {
364
365     fatalbox("sk_register");
366 }
367
368 Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
369               int nodelay, Plug plug)
370 {
371     static struct socket_function_table fn_table = {
372         mactcp_plug,
373         mactcp_close,
374         mactcp_write,
375         mactcp_write_oob,
376         mactcp_flush,
377         mactcp_set_private_ptr,
378         mactcp_get_private_ptr,
379         mactcp_set_frozen,
380         mactcp_socket_error
381     };
382     TCPiopb pb;
383     UDPiopb upb;
384     Actual_Socket ret;
385     ip_addr dstaddr;
386     size_t buflen;
387
388     /*
389      * Create Socket structure.
390      */
391     ret = smalloc(sizeof(struct Socket_tag));
392     ret->s = 0;
393     ret->fn = &fn_table;
394     ret->err = noErr;
395     ret->plug = plug;
396     bufchain_init(&ret->output_data);
397     ret->connected = 0;                /* to start with */
398     ret->writable = 0;                 /* to start with */
399     ret->sending_oob = 0;
400     ret->frozen = 0;
401     ret->frozen_readable = 0;
402     ret->localhost_only = 0;           /* unused, but best init anyway */
403     ret->pending_error = 0;
404     ret->oobinline = oobinline;
405     ret->oobpending = FALSE;
406     ret->listener = 0;
407
408     dstaddr = addr->hostinfo.addr[0]; /* XXX should try all of them */
409     /*
410      * Create a TCP stream.
411      * 
412      * MacTCP requires us to provide it with some buffer memory.  Page
413      * 31 of the Programmer's Guide says it should be a minimum of
414      * 4*MTU+1024.  Page 36 says a minimum of 4096 bytes.  Assume
415      * they're both correct.
416      */
417     assert(addr->resolved);
418     upb.ioCRefNum = mactcp.refnum;
419     upb.csCode = UDPMaxMTUSize;
420     upb.csParam.mtu.remoteHost = dstaddr;
421     upb.csParam.mtu.userDataPtr = NULL;
422     ret->err = PBControlSync((ParmBlkPtr)&upb);
423     if (ret->err != noErr) return (Socket)ret;
424
425     buflen = upb.csParam.mtu.mtuSize * 4 + 1024;
426     if (buflen < 4096) buflen = 4096;
427     pb.ioCRefNum = mactcp.refnum;
428     pb.csCode = TCPCreate;
429     pb.csParam.create.rcvBuff = smalloc(buflen);
430     pb.csParam.create.rcvBuffLen = buflen;
431     pb.csParam.create.notifyProc = NULL;
432     pb.csParam.create.userDataPtr = (Ptr)ret;
433     ret->err = PBControlSync((ParmBlkPtr)&pb);
434     if (ret->err != noErr) return (Socket)ret;
435     ret->s = pb.tcpStream;
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     ret->next->prev = &ret->next;
470     mactcp.socklist = ret;
471
472     return (Socket)ret;
473 }
474
475 Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
476 {
477
478     fatalbox("sk_newlistener");
479 }
480
481 static void mactcp_close(Socket sock)
482 {
483     Actual_Socket s = (Actual_Socket)sock;
484     TCPiopb pb;
485
486     /*
487      * TCPClose is equivalent to shutdown(fd, SHUT_WR), and hence
488      * leaves the Rx side open, while TCPAbort seems rather vicious,
489      * throwing away Tx data that haven't been ACKed yet.  We do both
490      * in succession.
491      */
492     pb.ioCRefNum = mactcp.refnum;
493     pb.csCode = TCPClose;
494     pb.tcpStream = s->s;
495     pb.csParam.close.validityFlags = 0;
496     pb.csParam.close.userDataPtr = (Ptr)s;
497     s->err = PBControlSync((ParmBlkPtr)&pb);
498     /* Not much we can do about an error anyway. */
499
500     pb.ioCRefNum = mactcp.refnum;
501     pb.csCode = TCPAbort;
502     pb.tcpStream = s->s;
503     pb.csParam.abort.userDataPtr = (Ptr)s;
504     s->err = PBControlSync((ParmBlkPtr)&pb);
505     /* Even less we can do about an error here. */
506
507     pb.ioCRefNum = mactcp.refnum;
508     pb.csCode = TCPRelease;
509     pb.tcpStream = s->s;
510     pb.csParam.create.userDataPtr = (Ptr)s;
511     s->err = PBControlSync((ParmBlkPtr)&pb);
512     if (s->err == noErr)
513         sfree(pb.csParam.create.rcvBuff);
514
515     /* Unhitch from list of sockets */
516     *s->prev = s->next;
517     if (s->next != NULL)
518         s->next->prev = s->prev;
519
520     sfree(s);
521 }
522
523 static int mactcp_write(Socket sock, char *buf, int len)
524 {
525     Actual_Socket s = (Actual_Socket) sock;
526     wdsEntry wds[2];
527     TCPiopb pb;
528
529     wds[0].length = len;
530     wds[0].ptr = buf;
531     wds[1].length = 0;
532
533     pb.ioCRefNum = mactcp.refnum;
534     pb.csCode = TCPSend;
535     pb.tcpStream = s->s;
536     pb.csParam.send.validityFlags = 0;
537     pb.csParam.send.pushFlag = TRUE; /* XXX we want it to return. */
538     pb.csParam.send.urgentFlag = 0;
539     pb.csParam.send.wdsPtr = (Ptr)wds;
540     pb.csParam.send.userDataPtr = (Ptr)s;
541     s->err = PBControlSync((ParmBlkPtr)&pb);
542     return 0;
543 }
544
545 static int mactcp_write_oob(Socket sock, char *buf, int len)
546 {
547
548     fatalbox("mactcp_write_oob");
549 }
550
551 /*
552  * Called from our event loop if there's work to do.
553  */
554 void mactcp_poll(void)
555 {
556     Actual_Socket s;
557     TCPiopb pb;
558
559     for (s = mactcp.socklist; s != NULL; s = s->next) {
560         /* XXX above can't handle sockets being deleted. */
561         pb.ioCRefNum = mactcp.refnum;
562         pb.csCode = TCPStatus;
563         pb.tcpStream = s->s;
564         pb.csParam.status.userDataPtr = (Ptr)s;
565         s->err = PBControlSync((ParmBlkPtr)&pb);
566         if (s->err != noErr)
567             continue;
568         if (pb.csParam.status.amtUnreadData > 0)
569             mactcp_recv(s, pb.csParam.status.amtUnreadData);
570         /* Should check connectionState in case remote has closed */
571     }
572 }
573
574 static void mactcp_recv(Actual_Socket s, size_t len)
575 {
576     rdsEntry rds[2];
577     TCPiopb pb;
578
579     if (s->frozen) return;
580
581     while (len > 0) {
582         pb.ioCRefNum = mactcp.refnum;
583         pb.csCode = TCPNoCopyRcv;
584         pb.tcpStream = s->s;
585         pb.csParam.receive.commandTimeoutValue = 0;
586         pb.csParam.receive.rdsPtr = (Ptr)rds;
587         pb.csParam.receive.rdsLength = lenof(rds) - 1;
588         pb.csParam.receive.userDataPtr = (Ptr)s;
589         s->err = PBControlSync((ParmBlkPtr)&pb);
590         if (s->err != noErr)
591             return;
592         plug_receive(s->plug, 0, rds[0].ptr, rds[0].length);
593         len -= rds[0].length;
594         pb.csCode = TCPRcvBfrReturn;
595         s->err = PBControlSync((ParmBlkPtr)&pb);
596         if (s->err != noErr)
597             return;
598     }   
599 }
600
601 /*
602  * Each socket abstraction contains a `void *' private field in
603  * which the client can keep state.
604  */
605 static void mactcp_set_private_ptr(Socket sock, void *ptr)
606 {
607     Actual_Socket s = (Actual_Socket) sock;
608     s->private_ptr = ptr;
609 }
610
611 static void *mactcp_get_private_ptr(Socket sock)
612 {
613     Actual_Socket s = (Actual_Socket) sock;
614     return s->private_ptr;
615 }
616
617 /*
618  * Special error values are returned from sk_namelookup and sk_new
619  * if there's a problem. These functions extract an error message,
620  * or return NULL if there's no problem.
621  */
622 char *sk_addr_error(SockAddr addr)
623 {
624     static char buf[64];
625
626     switch (addr->hostinfo.rtnCode) {
627       case noErr:
628         return NULL;
629       case nameSyntaxErr:
630         return "Name syntax error";
631       case noNameServer:
632         return "No name server found";
633       case authNameErr:
634         return "Domain name does not exist";
635       case noAnsErr:
636         return "No answer from domain name server";
637       case dnrErr:
638         return "Domain name server returned an error";
639       case outOfMemory:
640         return "Out of memory";
641       default:
642         sprintf(buf, "Unknown DNR error %d", addr->hostinfo.rtnCode);
643         return buf;
644     }
645 }
646
647 static char *mactcp_socket_error(Socket sock)
648 {
649     static char buf[64];
650     Actual_Socket s = (Actual_Socket) sock;
651
652     switch (s->err) {
653       case noErr:
654         return NULL;
655       case insufficientResources:
656         return "Insufficient resources to open TCP stream";
657       case duplicateSocket:
658         return "Duplicate socket";
659       case openFailed:
660         return "Connection failed while opening";
661       default:
662         sprintf(buf, "Unknown MacTCP error %d", s->err);
663         return buf;
664     }
665 }
666
667 static void mactcp_set_frozen(Socket sock, int is_frozen)
668 {
669     Actual_Socket s = (Actual_Socket) sock;
670
671     if (s->frozen == is_frozen)
672         return;
673     s->frozen = is_frozen;
674 }
675
676 /*
677  * Bits below here would usually be in dnr.c, shipped with the MacTCP
678  * SDK, but its convenient not to require that, and since we assume
679  * System 7 we can actually simplify things a lot.
680  */
681
682 static OSErr OpenResolver(char *hosts_file)
683 {
684     short vrefnum;
685     long dirid;
686     HParamBlockRec pb;
687     Str255 filename;
688     OSErr err;
689     int fd;
690     Handle dnr_handle;
691
692     if (mactcp.dnr_handle != NULL)
693         return noErr;
694
695     err = FindFolder(kOnSystemDisk, kControlPanelFolderType, FALSE, &vrefnum,
696                      &dirid);
697     if (err != noErr) return err;
698
699     /*
700      * Might be better to use PBCatSearch here, but it's not always
701      * available.
702      */
703     pb.fileParam.ioCompletion = NULL;
704     pb.fileParam.ioNamePtr = filename;
705     pb.fileParam.ioVRefNum = vrefnum;
706     pb.fileParam.ioFDirIndex = 1;
707     pb.fileParam.ioDirID = dirid;
708     fd = -1;
709
710     while (PBHGetFInfoSync(&pb) == noErr) {
711         if (pb.fileParam.ioFlFndrInfo.fdType == 'cdev' &&
712             pb.fileParam.ioFlFndrInfo.fdCreator == 'ztcp') {
713             fd = HOpenResFile(vrefnum, dirid, filename, fsRdPerm);
714             if (fd == -1) continue;
715             dnr_handle = Get1IndResource('dnrp', 1);
716             if (dnr_handle != NULL)
717                 break;
718             CloseResFile(fd);
719             fd = -1;
720         }
721         pb.fileParam.ioDirID = dirid;
722         pb.fileParam.ioFDirIndex++;
723     }
724     if (fd == -1)
725         return fnfErr;
726     
727     DetachResource(dnr_handle);
728     CloseResFile(fd);
729
730     MoveHHi(dnr_handle);
731     HLock(dnr_handle);
732
733     err = InvokeOpenResolverUPP(OPENRESOLVER, hosts_file,
734                                 (OpenResolverUPP)*dnr_handle);
735     if (err != noErr) {
736         HUnlock(dnr_handle);
737         DisposeHandle(dnr_handle);
738         return err;
739     }
740
741     mactcp.dnr_handle = dnr_handle;
742     return noErr;
743 }
744
745 OSErr CloseResolver(void)
746 {
747     Handle dnr_handle = mactcp.dnr_handle;
748     OSErr err;
749
750     if (mactcp.dnr_handle == NULL)
751         return notOpenErr;
752
753     err = InvokeCloseResolverUPP(CLOSERESOLVER,
754                                  (CloseResolverUPP)*mactcp.dnr_handle);
755     if (err != noErr)
756         return err;
757
758     mactcp.dnr_handle = NULL;
759     HUnlock(dnr_handle);
760     DisposeHandle(dnr_handle);
761     return noErr;
762 }
763
764 /* MacTCP doesn't have a services database. */
765 int net_service_lookup(char *service)
766 {
767
768     return 0;
769 }
770
771
772 /*
773  * Local Variables:
774  * c-file-style: "simon"
775  * End:
776  */