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