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