]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - macnet.c
Mac networking ios still as shafted as ever, but I should probably commit what
[PuTTY.git] / macnet.c
1 /* $Id: macnet.c,v 1.1.2.4 1999/04/06 23:18:49 ben Exp $ */
2 /*
3  * Copyright (c) 1999 Ben Harris
4  * All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or
11  * sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  * 
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  * 
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27 /*
28  * macnet.c -- PuTTY-to-MacTCP glue
29  */
30
31 #include <MacTypes.h>
32 #include <AddressXlation.h>
33 #include <Devices.h>
34 #include <MacTCP.h>
35 #include <MixedMode.h>
36 #include <OSUtils.h>
37 #include <Processes.h>
38
39 #include <assert.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include "putty.h"
44
45 /*
46  * The theory behind this stuff:
47  *
48  * net_recv attempts to deliver any incoming data waiting on the
49  * queue.  Since MacTCP maintains a buffer for incoming data, there's
50  * no need for us to run asynchronous TCPRcvs, and we just do a
51  * synchronous one if we detect some data waiting.  Since TCPRcv can't
52  * be given a timeout of zero, we use TCPStatus to work out if there's
53  * anything waiting first.
54  *
55  * Sending data is trickier.  TCPSend reserves the right to block
56  * until everything we've sent is ACKed, which means we have to use it
57  * asynchronously.  In order to make life easier for backends, and to
58  * save a proliferation of circular buffers, we guarantee to take data
59  * off the hands of the backend as soon as it gives it to us.  This is
60  * reasonable because currently there's no way for the backend to say
61  * it can't take data, and once it's got them, it may as well give
62  * them to us.
63  * 
64  * Anyway, in order to avoid a fixed-size buffer overflowing, the send
65  * buffer is kept as a queue of blocks.  When net_send is called, we
66  * malloc a new block and stick it on the queue.  If the queue was
67  * empty, we kick off a new asynchronous TCPSend to handle our block.
68  *
69  */
70
71 typedef struct Socket {
72     TCPiopb iopb; /* current MacTCP operation */
73     TCPiopb spareiopb; /* for closing etc */
74     hostInfo hostinfo;
75     int port;
76     ProcessSerialNumber psn;
77     Session *s;
78 #if TARGET_CPU_68K && !TARGET_RT_CFM
79     long a5;
80 #endif
81     QHdr sendq; /* Blocks waiting to be sent */
82 } Socket;
83
84 typedef struct {
85     QElem qelem;
86     int flags;
87     wdsEntry wds;
88     short wdsterm;
89 } Send_Buffer;
90
91 /*
92  * Yes, I know the struct QElem has a short[1] to represent the user
93  * data.  I'm ignoring it because it makes my code prettier and
94  * improves the alignment.
95  */
96
97 typedef struct {
98     QElem qelem;
99     Socket *sock;
100     Net_Event_Type type;
101 } NetEvent;
102
103 #define TCPBUF_SIZE 8192
104
105 static QHdr macnet_eventq;
106 static QHdr macnet_freeq;
107
108 static short mtcp_refnum;
109 static int mtcp_initted = FALSE;
110
111 static OSErr macnet_init(void);
112 static pascal void macnet_resolved(hostInfo *, char *);
113 static void macnet_completed_open(TCPiopb*);
114 static void macnet_completed_send(TCPiopb*);
115 static void macnet_sent(Socket *);
116 static void macnet_startsend(Socket *);
117 static void macnet_completed_close(TCPiopb*);
118 static pascal void macnet_asr(StreamPtr, unsigned short, Ptr, unsigned short,
119                               ICMPReport *);
120 static void macnet_sendevent(Socket *, Net_Event_Type);
121
122 #if TARGET_RT_MAC_CFM
123 static RoutineDescriptor macnet_resolved_upp =
124     BUILD_ROUTINE_DESCRIPTOR(uppResultProcInfo, (ProcPtr)macnet_resolved);
125 static RoutineDescriptor macnet_completed_open_upp =
126     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
127                              (ProcPtr)macnet_completed_open);
128 static RoutineDescriptor macnet_complete_send_upp =
129     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
130                              (ProcPtr)macnet_completed_send);
131 static RoutineDescriptor macnet_completed_close_upp =
132     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
133                              (ProcPtr)macnet_completed_close);
134 static RoutineDescriptor macnet_asr_upp =
135     BUILD_ROUTINE_DESCRIPTOR(uppTCPNotifyProcInfo, (ProcPtr)macnet_asr);
136 #else
137 /* These handle A5 switching to thwart the optimiser. */
138 static pascal void macnet_resolved_upp(hostInfo *, char *);
139 static void macnet_completed_open_upp(TCPiopb *);
140 static void macnet_completed_send_upp(TCPiopb *);
141 static void macnet_completed_close_upp(TCPiopb *);
142 static pascal void macnet_asr_upp(StreamPtr, unsigned short, Ptr,
143                                   unsigned short, ICMPReport *);
144 #endif
145
146 /*
147  * Number of outstanding network events allowed. 
148  */
149 #define NUM_EVENTS 16
150
151 /*
152  * Initialise networking.  Set mtcp_initted if it goes OK.
153  */
154 static OSErr macnet_init(void) {
155     OSErr err;
156     NetEvent *eventblock;
157     int i;
158
159     /*
160      * FIXME: This is hideously broken, in that we're meant to faff
161      * with unit numbers and stuff, and we blatantly don't.
162      * On the other hand, neither does NCSA Telnet.  Hmm.
163      */
164     err = opendriver(".IPP", &mtcp_refnum);
165     if (err != noErr)
166         return err;
167     err = OpenResolver(NULL);
168     if (err != noErr)
169         return err;
170     /* Set up the event queues, and fill the free queue with events */
171     macnet_eventq.qFlags = 0;
172     macnet_eventq.qHead = macnet_eventq.qTail = NULL;
173     macnet_freeq.qFlags = 0;
174     macnet_freeq.qHead = macnet_eventq.qTail = NULL;
175     eventblock = smalloc(NUM_EVENTS * sizeof(NetEvent));
176     for (i = 0; i < NUM_EVENTS; i++)
177         Enqueue(&eventblock[i].qelem, &macnet_freeq);
178     mtcp_initted = TRUE;
179     return 0;
180 }
181
182 Socket *net_open(Session *s, char *host, int port) {
183     ip_addr a;
184     OSErr err = noErr;
185     Socket *sock;
186     void *tcpbuf;
187
188     /*
189      * First, get hold of all the memory we'll need (a lot of the
190      * later stuff happens at interrupt time)
191      */
192     sock = smalloc(sizeof(struct Socket));
193     memset(sock, 0, sizeof(*sock));
194     tcpbuf = smalloc(TCPBUF_SIZE);
195
196     /* Make a note of anything we don't want to forget */
197     sock->port = port;
198     GetCurrentProcess(&sock->psn);
199 #if TARGET_CPU_68K && !TARGET_RT_CFM
200     sock->a5 = SetCurrentA5();
201 #endif
202
203     /* Get MacTCP running if it's not already */
204     if (!mtcp_initted)
205         if ((err = macnet_init()) != noErr)
206             fatalbox("Couldn't init network (%d)", err);
207
208     /* Get ourselves a TCP stream to play with */
209     sock->iopb.ioCRefNum = mtcp_refnum;
210     sock->iopb.ioCompletion = NULL;
211     sock->iopb.csCode = TCPCreate;
212     sock->iopb.csParam.create.rcvBuff = tcpbuf;
213     sock->iopb.csParam.create.rcvBuffLen = TCPBUF_SIZE;
214     sock->iopb.csParam.create.notifyProc = macnet_asr_upp;
215     sock->iopb.csParam.create.userDataPtr = (Ptr)sock;
216     /* This could be done asynchronously, but I doubt it'll take long. */
217     err = PBControlSync((ParmBlkPtr)&sock->iopb);
218     if (err != noErr)
219         fatalbox("TCP stream create failed (%d)", err);
220
221     err = StrToAddr(host, &sock->hostinfo, &macnet_resolved_upp, (char *)sock);
222     /*
223      * A cache fault indicates that the DNR will call us back when
224      * it's found the host for us.
225      */
226     if (err != cacheFault)
227         macnet_resolved(&sock->hostinfo, (char *)sock);
228     return sock;
229 }
230
231 #if TARGET_CPU_68K && !TARGET_RT_CFM
232 static pascal void macnet_resolved_upp(hostInfo *hi, char *cookie) {
233     Socket *sock = (Socket *)cookie;
234     long olda5;
235
236     olda5 = SetA5(sock->a5);
237     macnet_resolved(hi, cookie);
238     SetA5(olda5);
239 }
240 #endif
241
242 static pascal void macnet_resolved(hostInfo *hi, char *cookie) {
243     Socket *sock = (Socket *)cookie;
244     OSErr err;
245
246     /*
247      * We've resolved a name, so now we'd like to connect to it (or
248      * report an error).
249      */
250     switch (sock->hostinfo.rtnCode) {
251       case noErr:
252         /* Open a connection */
253         sock->iopb.ioCompletion = macnet_completed_open_upp;
254         sock->iopb.csCode = TCPActiveOpen;
255         memset(&sock->iopb.csParam, 0, sizeof(sock->iopb.csParam));
256         sock->iopb.csParam.open.validityFlags = typeOfService;
257         sock->iopb.csParam.open.remoteHost = sock->hostinfo.addr[0]; /*XXX*/
258         sock->iopb.csParam.open.remotePort = sock->port;
259         sock->iopb.csParam.open.tosFlags = lowDelay;
260         sock->iopb.csParam.open.dontFrag = 0;
261         sock->iopb.csParam.open.timeToLive = 0; /* default */
262         sock->iopb.csParam.open.security = 0;
263         sock->iopb.csParam.open.optionCnt = 0;
264         sock->iopb.csParam.open.userDataPtr = (char *)sock;
265         err = PBControlAsync((ParmBlkPtr)&sock->iopb);
266         if (err != noErr)
267             macnet_sendevent(sock, NE_NOOPEN);
268         break;
269       default: /* Something went wrong */
270         macnet_sendevent(sock, NE_NOHOST);
271         break;
272     }
273 }
274
275 #if TARGET_CPU_68K && !TARGET_RT_CFM
276 static void macnet_completed_open_upp(TCPiopb *iopb) {
277     Socket *sock = (Socket *)iopb->csParam.open.userDataPtr;
278     long olda5;
279
280     olda5 = SetA5(sock->a5);
281     macnet_completed_open(iopb);
282     SetA5(olda5);
283 }
284 #endif
285
286 static void macnet_completed_open(TCPiopb *iopb) {
287     Socket *sock = (Socket *)iopb->csParam.open.userDataPtr;
288
289     switch (iopb->ioResult) {
290       case noErr:
291         macnet_sendevent(sock, NE_OPEN);
292         break;
293       default:
294         macnet_sendevent(sock, NE_NOOPEN);
295         break;
296     }
297 }
298
299 #if TARGET_CPU_68K && !TARGET_RT_CFM
300 static pascal void macnet_asr_upp(StreamPtr tcpstream,
301                                   unsigned short eventcode, Ptr cookie,
302                                   unsigned short terminreason, 
303                                   ICMPReport *icmpmsg) {
304     Socket *sock = (Socket *)cookie;
305     long olda5;
306
307     olda5 = SetA5(sock->a5);
308     macnet_asr(tcpstream, eventcode, cookie, terminreason, icmpmsg);
309     SetA5(olda5);
310 }
311 #endif
312
313 static pascal void macnet_asr(StreamPtr tcpstream, unsigned short eventcode,
314                               Ptr cookie, unsigned short terminreason,
315                               ICMPReport *icmpmsg) {
316     Socket *sock = (Socket *)cookie;
317
318     switch (eventcode) {
319       case TCPClosing:
320         macnet_sendevent(sock, NE_CLOSING);
321         break;
322       case TCPULPTimeout:
323         macnet_sendevent(sock, NE_TIMEOUT);
324         break;
325       case TCPTerminate:
326         switch (terminreason) {
327           case TCPRemoteAbort:
328             macnet_sendevent(sock, NE_ABORT);
329             break;
330           default:
331             macnet_sendevent(sock, NE_DIED);
332             break;
333         }
334         break;
335       case TCPDataArrival:
336         macnet_sendevent(sock, NE_DATA);
337         break;
338       case TCPUrgent:
339         macnet_sendevent(sock, NE_URGENT);
340         break;
341       case TCPICMPReceived:
342         switch (icmpmsg->reportType) {
343           case portUnreach:
344             macnet_sendevent(sock, NE_REFUSED);
345             break;
346         }
347         break;
348     }
349 }
350
351 /*
352  * Send a block of data.
353  */
354
355 int net_send(Socket *sock, void *buf, int buflen, int flags) {
356     OSErr err;
357     Send_Buffer *buff;
358
359     buff = smalloc(sizeof(Send_Buffer) + buflen);
360     buff->flags = flags;
361     buff->wds.length = buflen;
362     buff->wds.ptr = (Ptr)&buff[1]; /* after the end of the struct */
363     buff->wdsterm = 0;
364     memcpy(&buff[1], buf, buflen);
365     Enqueue(&buff->qelem, &sock->sendq);
366     /* Kick off the transmit if the queue was empty */
367     if (sock->sendq.qHead == &buff->qelem)
368         macnet_startsend(sock);
369 }
370
371 /*
372  * This is called once every time round the event loop to check for
373  * network events and handle them.
374  */
375 void macnet_eventcheck() {
376     NetEvent *ne;
377
378     if (!mtcp_initted)
379         return;
380     ne = (NetEvent *)macnet_eventq.qHead;
381     if (ne == NULL)
382         return;
383     Dequeue(&ne->qelem, &macnet_eventq);
384     switch (ne->type) {
385       case NE_SENT:
386         macnet_sent(ne->sock);
387         break;
388       default:
389         (ne->sock->s->back->msg)(ne->sock->s, ne->sock, ne->type);
390         break;
391     }
392     Enqueue(&ne->qelem, &macnet_freeq);
393 }
394
395 /*
396  * The block at the head of the send queue has finished sending, so we
397  * can free it.  Kick off the next transmission if there is one.
398  */
399 static void macnet_sent(Socket *sock) {
400     Send_Buffer *buff;
401
402     assert(sock->sendq.qHead != NULL);
403     buff = (Send_Buffer *)sock->sendq.qHead;
404     Dequeue(&buff->qelem, &sock->sendq);
405     sfree(buff);
406     if (sock->sendq.qHead != NULL)
407         macnet_startsend(sock);
408 }
409
410 /*
411  * There's a block on the head of the send queue which needs to be
412  * sent.
413  */
414
415 static void macnet_startsend(Socket *sock) {
416     Send_Buffer *buff;
417     OSErr err;
418
419     buff = (Send_Buffer *)sock->sendq.qHead;
420     sock->iopb.ioCompletion = macnet_completed_send_upp;
421     sock->iopb.csCode = TCPSend;
422     memset(&sock->iopb.csParam, 0, sizeof(sock->iopb.csParam));
423     sock->iopb.csParam.send.validityFlags = 0;
424     sock->iopb.csParam.send.pushFlag = buff->flags & SEND_PUSH ? true : false;
425     sock->iopb.csParam.send.urgentFlag = buff->flags & SEND_URG ? true : false;
426     sock->iopb.csParam.send.wdsPtr = (Ptr)&buff->wds;
427     sock->iopb.csParam.send.userDataPtr = (char *)sock;
428     err = PBControlAsync((ParmBlkPtr)&sock->iopb);
429 }
430
431 #if TARGET_CPU_68K && !TARGET_RT_CFM
432 static void macnet_completed_send_upp(TCPiopb *iopb) {
433     Socket *sock = (Socket *)iopb->csParam.send.userDataPtr;
434     long olda5;
435
436     olda5 = SetA5(sock->a5);
437     macnet_completed_send(iopb);
438     SetA5(olda5);
439 }
440 #endif
441
442 static void macnet_completed_send(TCPiopb *iopb) {
443     Socket *sock = (Socket *)iopb->csParam.send.userDataPtr;
444
445     switch (iopb->ioResult) {
446       case noErr:
447         macnet_sendevent(sock, NE_SENT);
448         break;
449       case connectionClosing:
450       case connectionTerminated:
451         /* We'll get an ASR, so ignore it here. */
452         break;
453       default:
454         macnet_sendevent(sock, NE_DIED);
455         break;
456     }
457 }
458
459
460 int net_recv(Socket *sock, void *buf, int buflen, int flags) {
461     TCPiopb iopb;
462     OSErr err;
463     int avail, want, got;
464
465     memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
466     /* Work out if there's anything to recieve (we don't want to block)  */
467     iopb.ioCompletion = NULL;
468     iopb.csCode = TCPStatus;
469     memset(&sock->iopb.csParam, 0, sizeof(sock->iopb.csParam));
470     err = PBControlSync((ParmBlkPtr)&iopb);
471     if (err != noErr)
472         return 0; /* macnet_asr should catch it anyway */
473     avail = iopb.csParam.status.amtUnreadData;
474     if (avail == 0)
475         return 0;
476     want = avail < buflen ? avail : buflen;
477     iopb.ioCompletion = NULL;
478     iopb.csCode = TCPRcv;
479     memset(&sock->iopb.csParam, 0, sizeof(sock->iopb.csParam));
480     iopb.csParam.receive.rcvBuff = buf;
481     iopb.csParam.receive.rcvBuffLen = want;
482     err = PBControlSync((ParmBlkPtr)&iopb);
483     if (err != noErr)
484         return 0;
485     return iopb.csParam.receive.rcvBuffLen;
486 }
487         
488
489 void net_close(Socket *sock) {
490     OSErr err;
491
492     /*
493      * This might get called in the middle of processing another
494      * request on the socket, so we have a spare parameter block for
495      * this purpose (allocating one dynamically would mean having to
496      * free it, which we can't do at interrupt time).
497      */
498     memcpy(&sock->spareiopb, &sock->iopb, sizeof(TCPiopb));
499     memset(&sock->spareiopb.csParam, 0, sizeof(sock->spareiopb.csParam));
500     sock->spareiopb.ioCompletion = macnet_completed_close_upp;
501     sock->spareiopb.csCode = TCPClose;
502     sock->spareiopb.csParam.close.validityFlags = 0;
503     sock->spareiopb.csParam.close.userDataPtr = (char *)sock;
504     err = PBControlAsync((ParmBlkPtr)&sock->spareiopb);
505     switch (err) {
506       case noErr:
507       case connectionClosing:
508       case connectionTerminated: /* We'll get an ASR */
509         break;
510       default:
511         macnet_sendevent(sock, NE_DIED);
512         break;
513     }
514 }
515
516 #if TARGET_CPU_68K && !TARGET_RT_CFM
517 static void macnet_completed_close_upp(TCPiopb* iopb) {
518     Socket *sock = (Socket *)iopb->csParam.close.userDataPtr;
519     long olda5;
520
521     olda5 = SetA5(sock->a5);
522     macnet_completed_close(iopb);
523     SetA5(olda5);
524 }
525 #endif
526
527 static void macnet_completed_close(TCPiopb* iopb) {
528     Socket *sock = (Socket *)iopb->csParam.close.userDataPtr;
529
530     switch (iopb->ioResult) {
531       case noErr:
532         macnet_sendevent(sock, NE_CLOSED);
533         break;
534       case connectionClosing:
535       case connectionTerminated:
536         break;
537       default:
538         macnet_sendevent(sock, NE_DIED);
539         break;
540     }
541 }
542
543 /*
544  * Free all the data structures associated with a socket and tear down
545  * any connection through it.
546  */
547 void net_destroy(Socket *sock) {
548     TCPiopb iopb;
549     OSErr err;
550
551     /*
552      * Yes, we need _another_ iopb, as there may be a send _and_ a
553      * close outstanding.  Luckily, destroying a socket is
554      * synchronous, so we can allocate this one dynamically.
555      */
556     memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
557     iopb.ioCompletion = NULL;
558     iopb.csCode = TCPRelease;
559     memset(&iopb.csParam, 0, sizeof(iopb.csParam));
560     err = PBControlSync((ParmBlkPtr)&iopb);
561     sfree(iopb.csParam.create.rcvBuff);
562     sfree(sock);
563 }
564
565 static void macnet_sendevent(Socket *sock, Net_Event_Type type) {
566     NetEvent *ne;
567
568     ne = (NetEvent *)macnet_freeq.qHead;
569     if (ne == NULL) return; /* It's a disaster, but how do we tell anyone? */
570     Dequeue(&ne->qelem, &macnet_freeq);
571     ne->sock = sock;
572     ne->type = type;
573     Enqueue(&ne->qelem, &macnet_eventq);
574     WakeUpProcess(&sock->psn);
575 }
576
577 /*
578  * Local Variables:
579  * c-file-style: "simon"
580  * End:
581  */