2 * Copyright (c) 2003 Ben Harris
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
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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
27 * mtcpnet.c - MacTCP interface
30 #if !TARGET_API_MAC_CARBON
37 #include <MixedMode.h>
38 #include <Resources.h>
42 #define DEFINE_PLUG_METHOD_MACROS
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.
54 static OSErr
OpenResolver(char *);
55 static OSErr
CloseResolver(void);
68 #define NUM_ALT_ADDRS 4
70 typedef struct hostInfo
{
73 unsigned long addr
[NUM_ALT_ADDRS
];
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 *)))
82 #define NewResultUPP(userRoutine) \
83 (ResultUPP)NewRoutineDescriptor((ProcPtr)(userRoutine), \
85 GetCurrentArchitecture())
86 #define DisposeResultUPP(userUPP) DisposeRoutineDescriptor(userUPP)
88 static OSErr
StrToAddr(char *, struct hostInfo
*, ResultUPP
*, char *);
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*)))
97 #define InvokeOpenResolverUPP(selector, fileName, userUPP) \
98 CALL_TWO_PARAMETER_UPP((userUPP), uppOpenResolverProcInfo, \
99 (selector), (fileName))
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
)))
107 #define InvokeCloseResolverUPP(selector, userUPP) \
108 CALL_ONE_PARAMETER_UPP((userUPP), uppCloseResolverProcInfo, (selector))
110 typedef CALLBACK_API_C(OSErr
, StrToAddrProcPtr
)(UInt32
, char *,
111 struct hostInfo
*, ResultUPP
,
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 *)))
122 #define InvokeStrToAddrUPP(selector, hostName, hostInfoPtr, ResultProc, \
123 userDataPtr, userUPP) \
124 CALL_FIVE_PARAMETER_UPP((userUPP), uppStrToAddrProcInfo, (selector),\
125 (hostName), (hostInfoPtr), (ResultProc), \
127 #define StrToAddr(hostName, hostInfoPtr, ResultProc, userDataPtr) \
128 InvokeStrToAddrUPP(STRTOADDR, hostName, hostInfoPtr, ResultProc, \
129 userDataPtr, (StrToAddrUPP)*mactcp.dnr_handle)
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 *)))
139 #define InvokeAddrToStrUPP(selector, addr, addrStr, userUPP) \
140 CALL_THREE_PARAMETER_UPP((userUPP), uppAddrToStrProcInfo, (selector),\
142 #define AddrToStr(addr, addrStr) \
143 InvokeAddrToStrUPP(ADDRTOSTR, addr, addrStr, \
144 (AddrToStrUPP)*mactcp.dnr_handle)
146 /* End of AddressXlation.h bits */
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
162 struct socket_function_table
*fn
;
163 /* the above variable absolutely *must* be the first in this structure */
168 bufchain output_data
;
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 */
177 int oobpending
; /* is there OOB data available to read? */
179 int pending_error
; /* in case send() returns error */
181 struct Socket_tag
*next
;
182 struct Socket_tag
**prev
;
186 * We used to typedef struct Socket_tag *Socket.
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.
193 typedef struct Socket_tag
*Actual_Socket
;
195 struct SockAddr_tag
{
197 struct hostInfo hostinfo
;
201 /* Global variables */
206 ProcessSerialNumber self
;
207 Actual_Socket socklist
;
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 char *mactcp_socket_error(Socket
);
221 static void mactcp_set_frozen(Socket
, int);
223 static void mactcp_recv(Actual_Socket s
, size_t len
);
227 * This should be called once before any TCP connection is opened.
230 OSErr
mactcp_init(void)
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)
241 err
= OpenDriver("\p.IPP", &mactcp
.refnum
);
242 if (err
!= noErr
) return err
;
243 err
= OpenResolver(NULL
);
244 if (err
!= noErr
) return err
;
246 mactcp
.initialised
= TRUE
;
250 void mactcp_cleanup(void)
252 Actual_Socket s
, next
;
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.
259 * XXX What about in-flight aync I/O (when we support that)?
261 for (s
= mactcp
.socklist
; s
!= NULL
; s
= next
) {
262 next
= s
->next
; /* s is about to vanish */
263 mactcp_close(&s
->fn
);
267 * When we get async DNS, we have to wait for any outstanding
268 * requests to complete here before exiting.
271 mactcp
.initialised
= FALSE
;
274 static ResultUPP mactcp_lookupdone_upp
;
276 SockAddr
mactcp_namelookup(char const *host
, char **canonicalname
)
278 SockAddr ret
= smalloc(sizeof(struct SockAddr_tag
));
280 volatile int done
= FALSE
;
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
,
292 * PuTTY expects DNS lookups to be synchronous (see bug
293 * "async-dns"), so we pretend they are.
295 if (err
== cacheFault
)
298 ret
->resolved
= TRUE
;
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';
308 *canonicalname
= smalloc(1+strlen(realhost
));
309 strcpy(*canonicalname
, realhost
);
313 static pascal void mactcp_lookupdone(struct hostInfo
*hi
, char *cookie
)
315 volatile int *donep
= (int *)cookie
;
320 SockAddr
mactcp_nonamelookup(char const *host
)
322 SockAddr ret
= smalloc(sizeof(struct SockAddr_tag
));
324 ret
->resolved
= FALSE
;
325 ret
->hostinfo
.rtnCode
= noErr
;
326 ret
->hostname
[0] = '\0';
327 strncat(ret
->hostname
, host
, lenof(ret
->hostname
) - 1);
331 void mactcp_getaddr(SockAddr addr
, char *buf
, int buflen
)
336 if (addr
->resolved
) {
337 /* XXX only return first address */
338 err
= AddrToStr(addr
->hostinfo
.addr
[0], mybuf
);
341 strncat(buf
, mybuf
, buflen
- 1);
344 strncat(buf
, addr
->hostname
, buflen
- 1);
348 /* I think "local" here really means "loopback" */
350 int mactcp_hostname_is_local(char *name
)
353 return !strcmp(name
, "localhost");
356 int mactcp_address_is_local(SockAddr addr
)
361 for (i
= 0; i
< NUM_ALT_ADDRS
; i
++)
362 if (addr
->hostinfo
.addr
[i
] & 0xff000000 == 0x7f000000)
367 int mactcp_addrtype(SockAddr addr
)
371 return ADDRTYPE_IPV4
;
372 return ADDRTYPE_NAME
;
375 void mactcp_addrcopy(SockAddr addr
, char *buf
)
378 /* XXX only return first address */
379 memcpy(buf
, &addr
->hostinfo
.addr
[0], 4);
382 void mactcp_addr_free(SockAddr addr
)
388 static Plug
mactcp_plug(Socket sock
, Plug p
)
390 Actual_Socket s
= (Actual_Socket
) sock
;
398 static void mactcp_flush(Socket s
)
401 fatalbox("mactcp_flush");
404 Socket
mactcp_register(void *sock
, Plug plug
)
407 fatalbox("mactcp_register");
410 static TCPNotifyUPP mactcp_asr_upp
;
412 Socket
mactcp_new(SockAddr addr
, int port
, int privport
, int oobinline
,
413 int nodelay
, Plug plug
)
415 static struct socket_function_table fn_table
= {
421 mactcp_set_private_ptr
,
422 mactcp_get_private_ptr
,
433 * Create Socket structure.
435 ret
= smalloc(sizeof(struct Socket_tag
));
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;
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
;
452 dstaddr
= addr
->hostinfo
.addr
[0]; /* XXX should try all of them */
454 * Create a TCP stream.
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.
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
;
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
= smalloc(buflen
);
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
;
485 * Open the connection.
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
;
500 ret
->err
= PBControlSync((ParmBlkPtr
)&pb
);
501 if (!privport
|| ret
->err
!= duplicateSocket
)
503 pb
.csParam
.open
.localPort
--;
504 if (pb
.csParam
.open
.localPort
== 0)
508 if (ret
->err
!= noErr
) return (Socket
)ret
;
510 ret
->connected
= TRUE
;
511 ret
->writable
= TRUE
;
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
;
523 Socket
mactcp_newlistener(char *srcaddr
, int port
, Plug plug
,
527 fatalbox("mactcp_newlistener");
530 static void mactcp_close(Socket sock
)
532 Actual_Socket s
= (Actual_Socket
)sock
;
536 * TCPClose is equivalent to shutdown(fd, SHUT_WR), and hence
537 * leaves the Rx side open, while TCPAbort seems rather vicious,
538 * throwing away Tx data that haven't been ACKed yet. We do both
541 pb
.ioCRefNum
= mactcp
.refnum
;
542 pb
.csCode
= TCPClose
;
544 pb
.csParam
.close
.validityFlags
= 0;
545 pb
.csParam
.close
.userDataPtr
= (Ptr
)s
;
546 s
->err
= PBControlSync((ParmBlkPtr
)&pb
);
547 /* Not much we can do about an error anyway. */
549 pb
.ioCRefNum
= mactcp
.refnum
;
550 pb
.csCode
= TCPAbort
;
552 pb
.csParam
.abort
.userDataPtr
= (Ptr
)s
;
553 s
->err
= PBControlSync((ParmBlkPtr
)&pb
);
554 /* Even less we can do about an error here. */
556 pb
.ioCRefNum
= mactcp
.refnum
;
557 pb
.csCode
= TCPRelease
;
559 pb
.csParam
.create
.userDataPtr
= (Ptr
)s
;
560 s
->err
= PBControlSync((ParmBlkPtr
)&pb
);
562 sfree(pb
.csParam
.create
.rcvBuff
);
564 /* Unhitch from list of sockets */
567 s
->next
->prev
= s
->prev
;
572 static int mactcp_write(Socket sock
, char const *buf
, int len
)
574 Actual_Socket s
= (Actual_Socket
) sock
;
579 * Casting away const from buf should be safe -- MacTCP won't
583 wds
[0].ptr
= (char *)buf
;
586 pb
.ioCRefNum
= mactcp
.refnum
;
589 pb
.csParam
.send
.validityFlags
= 0;
590 pb
.csParam
.send
.pushFlag
= TRUE
; /* XXX we want it to return. */
591 pb
.csParam
.send
.urgentFlag
= 0;
592 pb
.csParam
.send
.wdsPtr
= (Ptr
)wds
;
593 pb
.csParam
.send
.userDataPtr
= (Ptr
)s
;
594 s
->err
= PBControlSync((ParmBlkPtr
)&pb
);
598 static int mactcp_write_oob(Socket sock
, char const *buf
, int len
)
601 fatalbox("mactcp_write_oob");
604 static pascal void mactcp_asr(StreamPtr str
, unsigned short event
, Ptr cookie
,
605 unsigned short termin_reason
,
606 struct ICMPReport
*icmp
)
609 WakeUpProcess(&mactcp
.self
);
613 * Called from our event loop if there's work to do.
615 void mactcp_poll(void)
617 Actual_Socket s
, next
;
620 for (s
= mactcp
.socklist
; s
!= NULL
; s
= next
) {
623 pb
.ioCRefNum
= mactcp
.refnum
;
624 pb
.csCode
= TCPStatus
;
626 pb
.csParam
.status
.userDataPtr
= (Ptr
)s
;
627 s
->err
= PBControlSync((ParmBlkPtr
)&pb
);
630 if (pb
.csParam
.status
.amtUnreadData
== 0)
632 mactcp_recv(s
, pb
.csParam
.status
.amtUnreadData
);
634 switch (pb
.csParam
.status
.connectionState
) {
635 case TCPS_CLOSE_WAIT
:
636 /* Remote end has sent us a FIN */
637 plug_closing(s
->plug
, NULL
, 0, 0);
644 static void mactcp_recv(Actual_Socket s
, size_t len
)
649 if (s
->frozen
) return;
652 pb
.ioCRefNum
= mactcp
.refnum
;
653 pb
.csCode
= TCPNoCopyRcv
;
655 pb
.csParam
.receive
.commandTimeoutValue
= 0;
656 pb
.csParam
.receive
.rdsPtr
= (Ptr
)rds
;
657 pb
.csParam
.receive
.rdsLength
= lenof(rds
) - 1;
658 pb
.csParam
.receive
.userDataPtr
= (Ptr
)s
;
659 s
->err
= PBControlSync((ParmBlkPtr
)&pb
);
662 plug_receive(s
->plug
, 0, rds
[0].ptr
, rds
[0].length
);
663 len
-= rds
[0].length
;
664 pb
.csCode
= TCPRcvBfrReturn
;
665 s
->err
= PBControlSync((ParmBlkPtr
)&pb
);
672 * Each socket abstraction contains a `void *' private field in
673 * which the client can keep state.
675 static void mactcp_set_private_ptr(Socket sock
, void *ptr
)
677 Actual_Socket s
= (Actual_Socket
) sock
;
678 s
->private_ptr
= ptr
;
681 static void *mactcp_get_private_ptr(Socket sock
)
683 Actual_Socket s
= (Actual_Socket
) sock
;
684 return s
->private_ptr
;
688 * Special error values are returned from mactcp_namelookup and
689 * mactcp_new if there's a problem. These functions extract an error
690 * message, or return NULL if there's no problem.
692 char *mactcp_addr_error(SockAddr addr
)
696 switch (addr
->hostinfo
.rtnCode
) {
700 return "Name syntax error";
702 return "No name server found";
704 return "Domain name does not exist";
706 return "No answer from domain name server";
708 return "Domain name server returned an error";
710 return "Out of memory";
712 sprintf(buf
, "Unknown DNR error %d", addr
->hostinfo
.rtnCode
);
717 static char *mactcp_socket_error(Socket sock
)
720 Actual_Socket s
= (Actual_Socket
) sock
;
725 case insufficientResources
:
726 return "Insufficient resources to open TCP stream";
727 case duplicateSocket
:
728 return "Duplicate socket";
730 return "Connection failed while opening";
732 sprintf(buf
, "Unknown MacTCP error %d", s
->err
);
737 static void mactcp_set_frozen(Socket sock
, int is_frozen
)
739 Actual_Socket s
= (Actual_Socket
) sock
;
741 if (s
->frozen
== is_frozen
)
743 s
->frozen
= is_frozen
;
747 * Bits below here would usually be in dnr.c, shipped with the MacTCP
748 * SDK, but its convenient not to require that, and since we assume
749 * System 7 we can actually simplify things a lot.
752 static OSErr
OpenResolver(char *hosts_file
)
762 if (mactcp
.dnr_handle
!= NULL
)
765 err
= FindFolder(kOnSystemDisk
, kControlPanelFolderType
, FALSE
, &vrefnum
,
767 if (err
!= noErr
) return err
;
770 * Might be better to use PBCatSearch here, but it's not always
773 pb
.fileParam
.ioCompletion
= NULL
;
774 pb
.fileParam
.ioNamePtr
= filename
;
775 pb
.fileParam
.ioVRefNum
= vrefnum
;
776 pb
.fileParam
.ioFDirIndex
= 1;
777 pb
.fileParam
.ioDirID
= dirid
;
780 while (PBHGetFInfoSync(&pb
) == noErr
) {
781 if (pb
.fileParam
.ioFlFndrInfo
.fdType
== 'cdev' &&
782 pb
.fileParam
.ioFlFndrInfo
.fdCreator
== 'ztcp') {
783 fd
= HOpenResFile(vrefnum
, dirid
, filename
, fsRdPerm
);
784 if (fd
== -1) continue;
785 dnr_handle
= Get1IndResource('dnrp', 1);
786 if (dnr_handle
!= NULL
)
791 pb
.fileParam
.ioDirID
= dirid
;
792 pb
.fileParam
.ioFDirIndex
++;
797 DetachResource(dnr_handle
);
803 err
= InvokeOpenResolverUPP(OPENRESOLVER
, hosts_file
,
804 (OpenResolverUPP
)*dnr_handle
);
807 DisposeHandle(dnr_handle
);
811 mactcp
.dnr_handle
= dnr_handle
;
815 OSErr
CloseResolver(void)
817 Handle dnr_handle
= mactcp
.dnr_handle
;
820 if (mactcp
.dnr_handle
== NULL
)
823 err
= InvokeCloseResolverUPP(CLOSERESOLVER
,
824 (CloseResolverUPP
)*mactcp
.dnr_handle
);
828 mactcp
.dnr_handle
= NULL
;
830 DisposeHandle(dnr_handle
);
838 * c-file-style: "simon"