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