]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - macnet.c
Lots of MacTCP networking stuff. I think all the code's written, but it
[PuTTY.git] / macnet.c
1 /* $Id: macnet.c,v 1.1.2.3 1999/04/04 18:23:34 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     long a5;
79     QHdr sendq; /* Blocks waiting to be sent */
80 } Socket;
81
82 typedef struct {
83     QElem qelem;
84     int flags;
85     wdsEntry wds;
86     short wdsterm;
87 } Send_Buffer;
88
89 /*
90  * Yes, I know the struct QElem has a short[1] to represent the user
91  * data.  I'm ignoring it because it makes my code prettier and
92  * improves the alignment.
93  */
94
95 typedef struct {
96     QElem qelem;
97     Socket *sock;
98     Net_Event_Type type;
99 } NetEvent;
100
101 #define TCPBUF_SIZE 8192
102
103 static QHdr macnet_eventq;
104 static QHdr macnet_freeq;
105
106 static short mtcp_refnum;
107 static int mtcp_initted = FALSE;
108
109 static OSErr macnet_init(void);
110 static pascal void macnet_resolved(hostInfo *, char *);
111 static void macnet_completed_open(TCPiopb*);
112 static void macnet_completed_send(TCPiopb*);
113 static void macnet_sent(Socket *);
114 static void macnet_startsend(Socket *);
115 static void macnet_completed_close(TCPiopb*);
116 static pascal void macnet_asr(StreamPtr, unsigned short, Ptr, unsigned short,
117                               ICMPReport *);
118 static void macnet_sendevent(Socket *, Net_Event_Type);
119
120 #if TARGET_RT_MAC_CFM
121 static RoutineDescriptor macnet_resolved_upp =
122     BUILD_ROUTINE_DESCRIPTOR(uppResultProcInfo, (ProcPtr)macnet_resolved);
123 static RoutineDescriptor macnet_completed_open_upp =
124     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
125                              (ProcPtr)macnet_completed_open);
126 static RoutineDescriptor macnet_complete_send_upp =
127     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
128                              (ProcPtr)macnet_completed_send);
129 static RoutineDescriptor macnet_completed_close_upp =
130     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
131                              (ProcPtr)macnet_completed_close);
132 static RoutineDescriptor macnet_asr_upp =
133     BUILD_ROUTINE_DESCRIPTOR(uppTCPNotifyProcInfo, (ProcPtr)macnet_asr);
134 #else
135 #define macnet_resolved_upp macnet_resolved
136 #define macnet_completed_open_upp macnet_completed_open
137 #define macnet_completed_send_upp macnet_completed_send
138 #define macnet_completed_close_upp macnet_completed_close
139 #define macnet_asr_upp macnet_asr
140 #endif
141
142 /*
143  * Number of outstanding network events allowed. 
144  */
145 #define NUM_EVENTS 16
146
147 /*
148  * Initialise networking.  Set mtcp_initted if it goes OK.
149  */
150 static OSErr macnet_init(void) {
151     OSErr err;
152     NetEvent *eventblock;
153     int i;
154
155     /*
156      * FIXME: This is hideously broken, in that we're meant to faff
157      * with unit numbers and stuff, and we blatantly don't.
158      */
159     err = opendriver(".IPP", &mtcp_refnum);
160     if (err != noErr)
161         return err;
162     err = OpenResolver(NULL);
163     if (err != noErr)
164         return err;
165     /* Set up the event queues, and fill the free queue with events 
166      */
167     macnet_eventq.qFlags = 0;
168     macnet_eventq.qHead = macnet_eventq.qTail = NULL;
169     macnet_freeq.qFlags = 0;
170     macnet_freeq.qHead = macnet_eventq.qTail = NULL;
171     eventblock = smalloc(NUM_EVENTS * sizeof(NetEvent));
172     for (i = 0; i < NUM_EVENTS; i++)
173         Enqueue(&eventblock[i].qelem, &macnet_freeq);
174     mtcp_initted = TRUE;
175     return 0;
176 }
177
178 Socket *net_open(Session *s, char *host, int port) {
179     ip_addr a;
180     OSErr err = noErr;
181     Socket *sock;
182     void *tcpbuf;
183
184     /*
185      * First, get hold of all the memory we'll need (a lot of the
186      * later stuff happens at interrupt time)
187      */
188     sock = smalloc(sizeof(struct Socket));
189     memset(sock, 0, sizeof(*sock));
190     tcpbuf = smalloc(TCPBUF_SIZE);
191
192     /* Make a note of anything we don't want to forget */
193     sock->port = port;
194     GetCurrentProcess(&sock->psn);
195     sock->a5 = SetCurrentA5();
196
197     /* Get MacTCP running if it's not already */
198     if (!mtcp_initted)
199         if ((err = macnet_init()) != noErr)
200             fatalbox("Couldn't init network (%d)", err);
201
202     /* Get ourselves a TCP stream to play with */
203     sock->iopb.ioCRefNum = mtcp_refnum;
204     sock->iopb.csCode = TCPCreate;
205     sock->iopb.csParam.create.rcvBuff = tcpbuf;
206     sock->iopb.csParam.create.rcvBuffLen = TCPBUF_SIZE;
207     sock->iopb.csParam.create.notifyProc = macnet_asr_upp;
208     sock->iopb.csParam.create.userDataPtr = (Ptr)sock;
209     /* This could be done asynchronously, but I doubt it'll take long. */
210     err = PBControlSync((ParmBlkPtr)&sock->iopb);
211     if (err != noErr)
212         fatalbox("TCP stream open failed (%d)", err);
213
214     err = StrToAddr(host, &sock->hostinfo, &macnet_resolved_upp, (char *)sock);
215     if (err != noErr)
216         fatalbox("Host lookup failed (%d)", err);
217     if (sock->hostinfo.rtnCode != cacheFault)
218         macnet_resolved(&sock->hostinfo, (char *)sock);
219     return sock;
220 }
221
222 static pascal void macnet_resolved(hostInfo *hi, char *cookie) {
223     Socket *sock = (Socket *)cookie;
224     OSErr err;
225 #if !TARGET_RT_CFM
226     long olda5;
227
228     olda5 = SetA5(sock->a5);
229 #endif
230     /*
231      * We've resolved a name, so now we'd like to connect to it (or
232      * report an error).
233      */
234     switch (sock->hostinfo.rtnCode) {
235       case noErr:
236         /* Open a connection */
237         sock->iopb.ioCompletion = macnet_completed_open_upp;
238         sock->iopb.csCode = TCPActiveOpen;
239         sock->iopb.csParam.open.validityFlags = typeOfService;
240         sock->iopb.csParam.open.commandTimeoutValue = 0; /* unused */
241         sock->iopb.csParam.open.remoteHost = sock->hostinfo.addr[0]; /*XXX*/
242         sock->iopb.csParam.open.remotePort = sock->port;
243         /* localHost is set by MacTCP. */
244         sock->iopb.csParam.open.localPort = 0;
245         sock->iopb.csParam.open.tosFlags = lowDelay;
246         sock->iopb.csParam.open.dontFrag = 0;
247         sock->iopb.csParam.open.timeToLive = 0; /* default */
248         sock->iopb.csParam.open.security = 0;
249         sock->iopb.csParam.open.optionCnt = 0;
250         sock->iopb.csParam.open.userDataPtr = (char *)sock;
251         err = PBControlSync((ParmBlkPtr)&sock->iopb);
252         if (err != noErr)
253             macnet_sendevent(sock, NE_NOOPEN);
254         break;
255       default: /* Something went wrong */
256         macnet_sendevent(sock, NE_NOHOST);
257         break;
258     }
259 #if !TARGET_RT_CFM
260     SetA5(olda5);
261 #endif
262 }
263
264 static void macnet_completed_open(TCPiopb *iopb) {
265     Socket *sock = (Socket *)iopb->csParam.open.userDataPtr;
266 #if !TARGET_RT_CFM
267     long olda5;
268
269     olda5 = SetA5(sock->a5);
270 #endif
271     switch (iopb->ioResult) {
272       case noErr:
273         macnet_sendevent(sock, NE_OPEN);
274         break;
275       default:
276         macnet_sendevent(sock, NE_NOOPEN);
277         break;
278     }
279 #if !TARGET_RT_CFM
280     SetA5(olda5);
281 #endif
282 }
283
284 static pascal void macnet_asr(StreamPtr tcpstream, unsigned short eventcode,
285                               Ptr cookie, unsigned short terminreason,
286                               ICMPReport *icmpmsg) {
287     Socket *sock = (Socket *)cookie;
288 #if !TARGET_RT_CFM
289     long olda5;
290
291     olda5 = SetA5(sock->a5);
292 #endif
293     switch (eventcode) {
294       case TCPClosing:
295         macnet_sendevent(sock, NE_CLOSING);
296         break;
297       case TCPULPTimeout:
298         macnet_sendevent(sock, NE_TIMEOUT);
299         break;
300       case TCPTerminate:
301         switch (terminreason) {
302           case TCPRemoteAbort:
303             macnet_sendevent(sock, NE_ABORT);
304             break;
305           default:
306             macnet_sendevent(sock, NE_DIED);
307             break;
308         }
309         break;
310       case TCPDataArrival:
311         macnet_sendevent(sock, NE_DATA);
312         break;
313       case TCPUrgent:
314         macnet_sendevent(sock, NE_URGENT);
315         break;
316       case TCPICMPReceived:
317         switch (icmpmsg->reportType) {
318           case portUnreach:
319             macnet_sendevent(sock, NE_REFUSED);
320             break;
321         }
322         break;
323     }
324 #if !TARGET_RT_CFM
325     SetA5(olda5);
326 #endif
327 }
328
329 /*
330  * Send a block of data.
331  */
332
333 int net_send(Socket *sock, void *buf, int buflen, int flags) {
334     OSErr err;
335     Send_Buffer *buff;
336
337     buff = smalloc(sizeof(Send_Buffer) + buflen);
338     buff->flags = flags;
339     buff->wds.length = buflen;
340     buff->wds.ptr = (Ptr)&buff[1]; /* after the end of the struct */
341     buff->wdsterm = 0;
342     memcpy(&buff[1], buf, buflen);
343     Enqueue(&buff->qelem, &sock->sendq);
344     /* Kick off the transmit if the queue was empty */
345     if (sock->sendq.qHead == &buff->qelem)
346         macnet_startsend(sock);
347 }
348
349 /*
350  * This is called once every time round the event loop to check for
351  * network events and handle them.
352  */
353 void macnet_eventcheck() {
354     NetEvent *ne;
355
356     if (!mtcp_initted)
357         return;
358     ne = (NetEvent *)macnet_eventq.qHead;
359     if (ne == NULL)
360         return;
361     Dequeue(&ne->qelem, &macnet_eventq);
362     switch (ne->type) {
363       case NE_SENT:
364         macnet_sent(ne->sock);
365         break;
366       default:
367         (ne->sock->s->back->msg)(ne->sock->s, ne->sock, ne->type);
368         break;
369     }
370     Enqueue(&ne->qelem, &macnet_freeq);
371 }
372
373 /*
374  * The block at the head of the send queue has finished sending, so we
375  * can free it.  Kick off the next transmission if there is one.
376  */
377 static void macnet_sent(Socket *sock) {
378     Send_Buffer *buff;
379
380     assert(sock->sendq.qHead != NULL);
381     buff = (Send_Buffer *)sock->sendq.qHead;
382     Dequeue(&buff->qelem, &sock->sendq);
383     sfree(buff);
384     if (sock->sendq.qHead != NULL)
385         macnet_startsend(sock);
386 }
387
388 /*
389  * There's a block on the head of the send queue which needs to be
390  * sent.
391  */
392
393 static void macnet_startsend(Socket *sock) {
394     Send_Buffer *buff;
395     OSErr err;
396
397     buff = (Send_Buffer *)sock->sendq.qHead;
398     sock->iopb.csCode = TCPSend;
399     sock->iopb.csParam.send.validityFlags = 0;
400     sock->iopb.csParam.send.pushFlag = buff->flags & SEND_PUSH ? true : false;
401     sock->iopb.csParam.send.urgentFlag = buff->flags & SEND_URG ? true : false;
402     sock->iopb.csParam.send.wdsPtr = (Ptr)&buff->wds;
403     sock->iopb.csParam.send.userDataPtr = (char *)sock;
404     err = PBControlAsync((ParmBlkPtr)&sock->iopb);
405 }
406
407 int net_recv(Socket *sock, void *buf, int buflen, int flags) {
408     TCPiopb iopb;
409     OSErr err;
410     int avail, want, got;
411
412     memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
413     /* Work out if there's anything to recieve (we don't want to block) 
414  */
415     iopb.csCode = TCPStatus;
416     err = PBControlSync((ParmBlkPtr)&iopb);
417     if (err != noErr)
418         return 0; /* macnet_asr should catch it anyway */
419     avail = iopb.csParam.status.amtUnreadData;
420     if (avail == 0)
421         return 0;
422     want = avail < buflen ? avail : buflen;
423     iopb.csCode = TCPRcv;
424     iopb.csParam.receive.rcvBuff = buf;
425     iopb.csParam.receive.rcvBuffLen = want;
426     err = PBControlSync((ParmBlkPtr)&iopb);
427     if (err != noErr)
428         return 0;
429     return iopb.csParam.receive.rcvBuffLen;
430 }
431         
432
433 void net_close(Socket *sock) {
434     OSErr err;
435
436     /*
437      * This might get called in the middle of processing another
438      * request on the socket, so we have a spare parameter block for
439      * this purpose (allocating one dynamically would mean having to
440      * free it, which we can't do at interrupt time).
441      */
442     memcpy(&sock->spareiopb, &sock->iopb, sizeof(TCPiopb));
443     sock->spareiopb.ioCompletion = macnet_completed_close_upp;
444     sock->spareiopb.csCode = TCPClose;
445     sock->spareiopb.csParam.close.validityFlags = 0;
446     sock->spareiopb.csParam.close.userDataPtr = (char *)sock;
447     err = PBControlAsync((ParmBlkPtr)&sock->spareiopb);
448     switch (err) {
449       case noErr:
450       case connectionClosing:
451       case connectionTerminated: /* We'll get an ASR */
452         break;
453       default:
454         macnet_sendevent(sock, NE_DIED);
455         break;
456     }
457 }
458
459 static void macnet_completed_close(TCPiopb* iopb) {
460     Socket *sock = (Socket *)iopb->csParam.close.userDataPtr;
461 #if !TARGET_RT_CFM
462     long olda5;
463
464     olda5 = SetA5(sock->a5);
465 #endif
466     switch (iopb->ioResult) {
467       case noErr:
468         macnet_sendevent(sock, NE_CLOSED);
469         break;
470       case connectionClosing:
471       case connectionTerminated:
472         break;
473       default:
474         macnet_sendevent(sock, NE_DIED);
475         break;
476     }
477 #if !TARGET_RT_CFM
478     SetA5(olda5);
479 #endif
480 }
481
482 /*
483  * Free all the data structures associated with a socket and tear down
484  * any connection through it.
485  */
486 void net_destroy(Socket *sock) {
487     TCPiopb iopb;
488     OSErr err;
489
490     /*
491      * Yes, we need _another_ iopb, as there may be a send _and_ a
492      * close outstanding.  Luckily, destroying a socket is
493      * synchronous, so we can allocate this one dynamically.
494      */
495     memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
496     iopb.csCode = TCPRelease;
497     err = PBControlSync((ParmBlkPtr)&iopb);
498     sfree(iopb.csParam.create.rcvBuff);
499     sfree(sock);
500 }
501
502 static void macnet_sendevent(Socket *sock, Net_Event_Type type) {
503     NetEvent *ne;
504
505     ne = (NetEvent *)macnet_freeq.qHead;
506     assert (ne != NULL);
507     Dequeue(&ne->qelem, &macnet_freeq);
508     ne->sock = sock;
509     ne->type = type;
510     Enqueue(&ne->qelem, &macnet_eventq);
511     WakeUpProcess(&sock->psn);
512 }
513
514 /*
515  * Local Variables:
516  * c-file-style: "simon"
517  * End:
518  */