]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - macnet.c
059bb9e228ac266efd4a0e09aa39b92e9b6851e3
[PuTTY.git] / macnet.c
1 /* $Id: macnet.c,v 1.1.2.2 1999/04/03 21:53:29 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 //    unsigned char *inbuf;
77 //    int inbuf_head, inbuf_reap, inbuf_size;
78 //    unsigned char *outbuf;
79 //    int outbuf_head, outbuf_reap, outbuf_size;
80     ProcessSerialNumber psn;
81     Session *s;
82     UInt32 a5;
83     qHdr sendq; /* Blocks waiting to be sent */
84     qHdr freeq; /* Blocks sent, waiting to be freed */
85 } Socket;
86
87 typedef struct {
88     QElem qelem;
89     int flags;
90     int len;
91 } Send_Buffer;
92
93 /*
94  * Yes, I know the struct QElem has a short[1] to represent the user
95  * data.  I'm ignoring it because it makes my code prettier and
96  * improves the alignment.
97  */
98
99 typedef struct {
100     QElem qelem;
101     Socket *sock;
102     Net_Event_Type type;
103 } NetEvent;
104
105 #define TCPBUF_SIZE 8192
106
107 static QHdr macnet_eventq;
108 static QHdr macnet_freeq;
109
110 static short mtcp_refnum;
111 static int mtcp_initted = FALSE;
112
113 static OSErr macnet_init(void);
114 static pascal void macnet_resolved(hostInfo *, char *);
115 static void macnet_opened(TCPiopb*);
116 static void macnet_sent(TCPiopb*);
117 static void macnet_closed(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_opened_upp =
126     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
127                              (ProcPtr)macnet_opened);
128 static RoutineDescriptor macnet_sent_upp =
129     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo, (ProcPtr)macnet_sent);
130 static RoutineDescriptor macnet_closed_upp =
131     BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
132                              (ProcPtr)macnet_closed);
133 static RoutineDescriptor macnet_asr_upp =
134     BUILD_ROUTINE_DESCRIPTOR(uppTCPNotifyProcInfo, (ProcPtr)macnet_asr);
135 #else
136 #define macnet_resolved_upp macnet_resolved
137 #define macnet_opened_upp macnet_opened
138 #define macnet_sent_upp macnet_sent
139 #define macnet_closed_upp macnet_closed
140 #define macnet_asr_upp macnet_asr
141 #endif
142
143 /*
144  * Number of outstanding network events allowed. 
145  */
146 #define NUM_EVENTS 16
147
148 /*
149  * Initialise networking.  Set mtcp_initted if it goes OK.
150  */
151 static OSErr macnet_init(void) {
152     OSErr err;
153     NetEvent *eventblock;
154     int i;
155
156     err = opendriver(".IPP", &mtcp_refnum);
157     if (err != noErr)
158         return err;
159     err = OpenResolver(NULL);
160     if (err != noErr)
161         return err;
162     /* Set up the event queues, and fill the free queue with events */
163     macnet_eventq.qFlags = 0;
164     macnet_eventq.qHead = macnet_eventq.qTail = NULL;
165     macnet_freeq.qFlags = 0;
166     macnet_freeq.qHead = macnet_eventq.qTail = NULL;
167     eventblock = smalloc(NUM_EVENTS * sizeof(NetEvent));
168     for (i = 0; i < NUM_EVENTS; i++)
169         Enqueue(&eventblock[i].qelem, &macnet_freeq);
170     mtcp_initted = TRUE;
171 }
172
173 Socket *net_open(Session *s, char *host, int port) {
174     ip_addr a;
175     OSErr err = noErr;
176     Socket *sock;
177     void *tcpbuf;
178
179     /*
180      * First, get hold of all the memory we'll need (a lot of the
181      * later stuff happens at interrupt time)
182      */
183     sock = smalloc(sizeof(struct Socket));
184     memset(sock, 0, sizeof(*sock));
185     tcpbuf = smalloc(TCPBUF_SIZE);
186
187     /* Make a note of anything we don't want to forget */
188     sock->port = port;
189     GetCurrentProcess(&sock->psn);
190     sock->a5 = SetCurrentA5();
191
192     /* Get MacTCP running if it's not already */
193     if (!mtcp_initted)
194         if ((err = macnet_init()) != noErr)
195             fatalbox("Couldn't init network (%d)", err);
196
197     /* Get ourselves a TCP stream to play with */
198     sock->iopb.ioCRefNum = mtcp_refnum;
199     sock->iopb.csCode = TCPCreate;
200     sock->iopb.csParam.create.rcvBuff = tcpbuf;
201     sock->iopb.csParam.create.rcvBuffLen = TCPBUF_SIZE;
202     sock->iopb.csParam.create.notifyProc = macnet_asr_upp;
203     sock->iopb.csParam.create.userDataPtr = (Ptr)sock;
204     /* This could be done asynchronously, but I doubt it'll take long. */
205     err = PBControlSync((ParmBlkPtr)&sock->iopb);
206     if (err != noErr)
207         fatalbox("TCP stream open failed (%d)", err);
208
209     err = StrToAddr(host, &sock->hostinfo, &macnet_resolved_upp, (char *)sock);
210     if (err != noErr)
211         fatalbox("Host lookup failed (%d)", err);
212     if (sock->hostinfo.rtnCode != cacheFault)
213         macnet_resolved(&sock->hostinfo, (char *)sock);
214     return sock;
215 }
216
217 static pascal void macnet_resolved(hostInfo *hi, char *cookie) {
218     Socket *sock = (Socket *)cookie;
219     OSErr err;
220     UInt32 olda5;
221
222     olda5 = SetA5(sock->a5);
223     /*
224      * We've resolved a name, so now we'd like to connect to it (or
225      * report an error).
226      */
227     switch (sock->hostinfo.rtnCode) {
228       case noErr:
229         /* Open a connection */
230         sock->iopb.ioCompletion = macnet_opened_upp;
231         sock->iopb.csCode = TCPActiveOpen;
232         sock->iopb.csParam.open.validityFlags = typeOfService;
233         sock->iopb.csParam.open.commandTimeoutValue = 0; /* unused */
234         sock->iopb.csParam.open.remoteHost = sock->hostinfo.addr[0]; /*XXX*/
235         sock->iopb.csParam.open.remotePort = sock->port;
236         /* localHost is set by MacTCP. */
237         sock->iopb.csParam.open.localPort = 0;
238         sock->iopb.csParam.open.tosFlags = lowDelay;
239         sock->iopb.csParam.open.dontFrag = 0;
240         sock->iopb.csParam.open.timeToLive = 0; /* default */
241         sock->iopb.csParam.open.security = 0;
242         sock->iopb.csParam.open.optionCnt = 0;
243         sock->iopb.csParam.open.userDataPtr = (char *)sock;
244         err = PBControlSync((ParmBlkPtr)&sock->iopb);
245         if (err != noErr)
246             macnet_sendevent(sock, NE_NOOPEN);
247         break;
248       default: /* Something went wrong */
249         macnet_sendevent(sock, NE_NOHOST);
250         break;
251     }
252     SetA5(olda5);
253 }
254
255 static void macnet_opened(TCPiopb *iopb) {
256     Socket *sock = (Socket *)iopb->csParam.open.userDataPtr;
257     UInt32 olda5;
258
259     olda5 = SetA5(sock->a5);
260     switch (iopb->ioResult) {
261       case noErr:
262         macnet_sendevent(sock, NE_OPEN);
263         break;
264       default:
265         macnet_sendevent(sock, NE_NOOPEN);
266         break;
267     }
268     SetA5(olda5);
269 }
270
271 static pascal void macnet_asr(StreamPtr tcpstream, unsigned short eventcode,
272                               Ptr cookie, unsigned short terminreason,
273                               ICMPReport *icmpmsg) {
274     Socket *sock = (Socket *)cookie;
275     UInt32 olda5;
276
277     olda5 = SetA5(sock->a5);
278     switch (eventcode) {
279       case TCPClosing:
280         macnet_sendevent(sock, NE_CLOSING);
281         break;
282       case TCPULPTimeout:
283         macnet_sendevent(sock, NE_TIMEOUT);
284         break;
285       case TCPTerminate:
286         switch (terminreason) {
287           case TCPRemoteAbort:
288             macnet_sendevent(sock, NE_ABORT);
289             break;
290           default:
291             macnet_sendevent(sock, NE_DIED);
292             break;
293         }
294         break;
295       case TCPDataArrival:
296         macnet_sendevent(sock, NE_DATA);
297         break;
298       case TCPUrgent:
299         macnet_sendevent(sock, NE_URGENT);
300         break;
301       case TCPICMPReceived:
302         switch (icmpmsg->reportType) {
303           case portUnreach:
304             macnet_sendevent(sock, NE_REFUSED);
305             break;
306         }
307         break;
308     }
309     SetA5(olda5);
310 }
311
312 /*
313  * Send a block of data.
314  */
315
316 int net_send(Socket *sock, void *buf, int buflen, int flags) {{
317     OSErr err;
318     Send_Buffer *buff;
319
320     buff = smalloc(sizeof(Send_Buffer) + buflen);
321     buff->flags = flags;
322     buff->len = buflen;
323     memcpy(buff + 1, buf, buflen);
324     Enqueue(&buff->qelem, &sock->sendq);
325     macnet_start(sock);
326 }
327
328 int net_recv(Socket *sock, void *buf, int buflen, int flags) {
329     TCPiopb iopb;
330     OSErr err;
331     int avail, want, got;
332
333     memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
334     /* Work out if there's anything to recieve (we don't want to block) */
335     iopb.csCode = TCPStatus;
336     err = PBControlSync((ParmBlkPtr)&iopb);
337     if (err != noErr)
338         return 0; /* macnet_asr should catch it anyway */
339     avail = iopb.csParam.status.amtUnreadData;
340     if (avail == 0)
341         return 0;
342     want = avail < buflen ? avail : buflen;
343     iopb.csCode = TCPRcv;
344     iopb.csParam.receive.buffPtr = buf;
345     iopb.csParam.receive.buffLen = want;
346     err = PBControlSync((ParmBlkPtr)&iopb);
347     if (err != noErr)
348         return 0;
349     return iopb.csParam.receive.buffLen;
350 }
351         
352
353 void net_close(Socket *sock) {
354     OSErr err;
355
356     /*
357      * This might get called in the middle of processing another
358      * request on the socket, so we have a spare parameter block for
359      * this purpose (allocating one dynamically would mean having to
360      * free it, which we can't do at interrupt time).
361      */
362     memcpy(&sock->spareiopb, &sock->iopb, sizeof(TCPiopb));
363     sock->spareiopb.ioCompletion = macnet_closed_upp;
364     sock->spareiopb.csCode = TCPClose;
365     sock->spareiopb.csParam.close.validityFlags = 0;
366     sock->spareiopb.csParam.close.userDataPtr = (char *)sock;
367     err = PBControlAsync((ParmBlkPtr)&sock->spareiopb);
368     switch (err) {
369       case noErr:
370       case connectionClosing:
371       case connectionTerminated: /* We'll get an ASR */
372         break;
373       default:
374         macnet_sendevent(sock, NE_DIED);
375         break;
376     }
377 }
378
379 static void macnet_closed(TCPiopb* iopb) {
380     Socket *sock = (Socket *)iopb->csParam.close.userDataPtr;
381     UInt32 olda5;
382
383     olda5 = SetA5(sock->a5);
384     switch (iopb->ioResult) {
385       case noErr:
386         macnet_sendevent(sock, NE_CLOSED);
387         break;
388       case connectionClosing:
389       case connectionTerminated:
390         break;
391       default:
392         macnet_sendevent(sock, NE_DIED);
393         break;
394     }
395     SetA5(olda5);
396 }
397
398 /*
399  * Free all the data structures associated with a socket and tear down
400  * any connection through it.
401  */
402 void net_destroy(Socket *sock) {
403     TCPiopb iopb;
404     OSErr err;
405
406     /*
407      * Yes, we need _another_ iopb, as there may be a send _and_ a
408      * close outstanding.  Luckily, destroying a socket is
409      * synchronous, so we can allocate this one dynamically.
410      */
411     memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
412     iopb.csCode = TCPRelease;
413     err = PBControlSync((ParmBlkPtr)&iopb);
414     sfree(iopb.csParam.create.rcvBuff);
415     sfree(sock);
416 }
417
418 static void macnet_sendevent(Socket *sock, Net_Event_Type type) {
419     NetEvent *ne;
420
421     ne = (NetEvent *)macnet_freeq.qHead;
422     assert (ne != NULL);
423     Dequeue(&ne->qelem, &macnet_freeq);
424     ne->sock = sock;
425     ne->type = type;
426     Enqueue(&ne->qelem, &macnet_eventq);
427     WakeUpProcess(&sock->psn);
428 }
429
430 /*
431  * Local Variables:
432  * c-file-style: "simon"
433  * End:
434  */