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