1 /* $Id: macnet.c,v 1.1.2.4 1999/04/06 23:18:49 ben Exp $ */
3 * Copyright (c) 1999 Ben Harris
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
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
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
28 * macnet.c -- PuTTY-to-MacTCP glue
32 #include <AddressXlation.h>
35 #include <MixedMode.h>
37 #include <Processes.h>
46 * The theory behind this stuff:
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.
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
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.
71 typedef struct Socket {
72 TCPiopb iopb; /* current MacTCP operation */
73 TCPiopb spareiopb; /* for closing etc */
76 ProcessSerialNumber psn;
78 #if TARGET_CPU_68K && !TARGET_RT_CFM
81 QHdr sendq; /* Blocks waiting to be sent */
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.
103 #define TCPBUF_SIZE 8192
105 static QHdr macnet_eventq;
106 static QHdr macnet_freeq;
108 static short mtcp_refnum;
109 static int mtcp_initted = FALSE;
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,
120 static void macnet_sendevent(Socket *, Net_Event_Type);
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);
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 *);
147 * Number of outstanding network events allowed.
149 #define NUM_EVENTS 16
152 * Initialise networking. Set mtcp_initted if it goes OK.
154 static OSErr macnet_init(void) {
156 NetEvent *eventblock;
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.
164 err = opendriver(".IPP", &mtcp_refnum);
167 err = OpenResolver(NULL);
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);
182 Socket *net_open(Session *s, char *host, int port) {
189 * First, get hold of all the memory we'll need (a lot of the
190 * later stuff happens at interrupt time)
192 sock = smalloc(sizeof(struct Socket));
193 memset(sock, 0, sizeof(*sock));
194 tcpbuf = smalloc(TCPBUF_SIZE);
196 /* Make a note of anything we don't want to forget */
198 GetCurrentProcess(&sock->psn);
199 #if TARGET_CPU_68K && !TARGET_RT_CFM
200 sock->a5 = SetCurrentA5();
203 /* Get MacTCP running if it's not already */
205 if ((err = macnet_init()) != noErr)
206 fatalbox("Couldn't init network (%d)", err);
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);
219 fatalbox("TCP stream create failed (%d)", err);
221 err = StrToAddr(host, &sock->hostinfo, &macnet_resolved_upp, (char *)sock);
223 * A cache fault indicates that the DNR will call us back when
224 * it's found the host for us.
226 if (err != cacheFault)
227 macnet_resolved(&sock->hostinfo, (char *)sock);
231 #if TARGET_CPU_68K && !TARGET_RT_CFM
232 static pascal void macnet_resolved_upp(hostInfo *hi, char *cookie) {
233 Socket *sock = (Socket *)cookie;
236 olda5 = SetA5(sock->a5);
237 macnet_resolved(hi, cookie);
242 static pascal void macnet_resolved(hostInfo *hi, char *cookie) {
243 Socket *sock = (Socket *)cookie;
247 * We've resolved a name, so now we'd like to connect to it (or
250 switch (sock->hostinfo.rtnCode) {
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);
267 macnet_sendevent(sock, NE_NOOPEN);
269 default: /* Something went wrong */
270 macnet_sendevent(sock, NE_NOHOST);
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;
280 olda5 = SetA5(sock->a5);
281 macnet_completed_open(iopb);
286 static void macnet_completed_open(TCPiopb *iopb) {
287 Socket *sock = (Socket *)iopb->csParam.open.userDataPtr;
289 switch (iopb->ioResult) {
291 macnet_sendevent(sock, NE_OPEN);
294 macnet_sendevent(sock, NE_NOOPEN);
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;
307 olda5 = SetA5(sock->a5);
308 macnet_asr(tcpstream, eventcode, cookie, terminreason, icmpmsg);
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;
320 macnet_sendevent(sock, NE_CLOSING);
323 macnet_sendevent(sock, NE_TIMEOUT);
326 switch (terminreason) {
328 macnet_sendevent(sock, NE_ABORT);
331 macnet_sendevent(sock, NE_DIED);
336 macnet_sendevent(sock, NE_DATA);
339 macnet_sendevent(sock, NE_URGENT);
341 case TCPICMPReceived:
342 switch (icmpmsg->reportType) {
344 macnet_sendevent(sock, NE_REFUSED);
352 * Send a block of data.
355 int net_send(Socket *sock, void *buf, int buflen, int flags) {
359 buff = smalloc(sizeof(Send_Buffer) + buflen);
361 buff->wds.length = buflen;
362 buff->wds.ptr = (Ptr)&buff[1]; /* after the end of the struct */
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);
372 * This is called once every time round the event loop to check for
373 * network events and handle them.
375 void macnet_eventcheck() {
380 ne = (NetEvent *)macnet_eventq.qHead;
383 Dequeue(&ne->qelem, &macnet_eventq);
386 macnet_sent(ne->sock);
389 (ne->sock->s->back->msg)(ne->sock->s, ne->sock, ne->type);
392 Enqueue(&ne->qelem, &macnet_freeq);
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.
399 static void macnet_sent(Socket *sock) {
402 assert(sock->sendq.qHead != NULL);
403 buff = (Send_Buffer *)sock->sendq.qHead;
404 Dequeue(&buff->qelem, &sock->sendq);
406 if (sock->sendq.qHead != NULL)
407 macnet_startsend(sock);
411 * There's a block on the head of the send queue which needs to be
415 static void macnet_startsend(Socket *sock) {
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);
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;
436 olda5 = SetA5(sock->a5);
437 macnet_completed_send(iopb);
442 static void macnet_completed_send(TCPiopb *iopb) {
443 Socket *sock = (Socket *)iopb->csParam.send.userDataPtr;
445 switch (iopb->ioResult) {
447 macnet_sendevent(sock, NE_SENT);
449 case connectionClosing:
450 case connectionTerminated:
451 /* We'll get an ASR, so ignore it here. */
454 macnet_sendevent(sock, NE_DIED);
460 int net_recv(Socket *sock, void *buf, int buflen, int flags) {
463 int avail, want, got;
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);
472 return 0; /* macnet_asr should catch it anyway */
473 avail = iopb.csParam.status.amtUnreadData;
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);
485 return iopb.csParam.receive.rcvBuffLen;
489 void net_close(Socket *sock) {
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).
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);
507 case connectionClosing:
508 case connectionTerminated: /* We'll get an ASR */
511 macnet_sendevent(sock, NE_DIED);
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;
521 olda5 = SetA5(sock->a5);
522 macnet_completed_close(iopb);
527 static void macnet_completed_close(TCPiopb* iopb) {
528 Socket *sock = (Socket *)iopb->csParam.close.userDataPtr;
530 switch (iopb->ioResult) {
532 macnet_sendevent(sock, NE_CLOSED);
534 case connectionClosing:
535 case connectionTerminated:
538 macnet_sendevent(sock, NE_DIED);
544 * Free all the data structures associated with a socket and tear down
545 * any connection through it.
547 void net_destroy(Socket *sock) {
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.
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);
565 static void macnet_sendevent(Socket *sock, Net_Event_Type type) {
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);
573 Enqueue(&ne->qelem, &macnet_eventq);
574 WakeUpProcess(&sock->psn);
579 * c-file-style: "simon"