1 /* $Id: macnet.c,v 1.1.2.2 1999/04/03 21:53:29 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 // 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;
83 qHdr sendq; /* Blocks waiting to be sent */
84 qHdr freeq; /* Blocks sent, waiting to be freed */
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.
105 #define TCPBUF_SIZE 8192
107 static QHdr macnet_eventq;
108 static QHdr macnet_freeq;
110 static short mtcp_refnum;
111 static int mtcp_initted = FALSE;
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,
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_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);
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
144 * Number of outstanding network events allowed.
146 #define NUM_EVENTS 16
149 * Initialise networking. Set mtcp_initted if it goes OK.
151 static OSErr macnet_init(void) {
153 NetEvent *eventblock;
156 err = opendriver(".IPP", &mtcp_refnum);
159 err = OpenResolver(NULL);
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);
173 Socket *net_open(Session *s, char *host, int port) {
180 * First, get hold of all the memory we'll need (a lot of the
181 * later stuff happens at interrupt time)
183 sock = smalloc(sizeof(struct Socket));
184 memset(sock, 0, sizeof(*sock));
185 tcpbuf = smalloc(TCPBUF_SIZE);
187 /* Make a note of anything we don't want to forget */
189 GetCurrentProcess(&sock->psn);
190 sock->a5 = SetCurrentA5();
192 /* Get MacTCP running if it's not already */
194 if ((err = macnet_init()) != noErr)
195 fatalbox("Couldn't init network (%d)", err);
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);
207 fatalbox("TCP stream open failed (%d)", err);
209 err = StrToAddr(host, &sock->hostinfo, &macnet_resolved_upp, (char *)sock);
211 fatalbox("Host lookup failed (%d)", err);
212 if (sock->hostinfo.rtnCode != cacheFault)
213 macnet_resolved(&sock->hostinfo, (char *)sock);
217 static pascal void macnet_resolved(hostInfo *hi, char *cookie) {
218 Socket *sock = (Socket *)cookie;
222 olda5 = SetA5(sock->a5);
224 * We've resolved a name, so now we'd like to connect to it (or
227 switch (sock->hostinfo.rtnCode) {
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);
246 macnet_sendevent(sock, NE_NOOPEN);
248 default: /* Something went wrong */
249 macnet_sendevent(sock, NE_NOHOST);
255 static void macnet_opened(TCPiopb *iopb) {
256 Socket *sock = (Socket *)iopb->csParam.open.userDataPtr;
259 olda5 = SetA5(sock->a5);
260 switch (iopb->ioResult) {
262 macnet_sendevent(sock, NE_OPEN);
265 macnet_sendevent(sock, NE_NOOPEN);
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;
277 olda5 = SetA5(sock->a5);
280 macnet_sendevent(sock, NE_CLOSING);
283 macnet_sendevent(sock, NE_TIMEOUT);
286 switch (terminreason) {
288 macnet_sendevent(sock, NE_ABORT);
291 macnet_sendevent(sock, NE_DIED);
296 macnet_sendevent(sock, NE_DATA);
299 macnet_sendevent(sock, NE_URGENT);
301 case TCPICMPReceived:
302 switch (icmpmsg->reportType) {
304 macnet_sendevent(sock, NE_REFUSED);
313 * Send a block of data.
316 int net_send(Socket *sock, void *buf, int buflen, int flags) {{
320 buff = smalloc(sizeof(Send_Buffer) + buflen);
323 memcpy(buff + 1, buf, buflen);
324 Enqueue(&buff->qelem, &sock->sendq);
328 int net_recv(Socket *sock, void *buf, int buflen, int flags) {
331 int avail, want, got;
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);
338 return 0; /* macnet_asr should catch it anyway */
339 avail = iopb.csParam.status.amtUnreadData;
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);
349 return iopb.csParam.receive.buffLen;
353 void net_close(Socket *sock) {
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).
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);
370 case connectionClosing:
371 case connectionTerminated: /* We'll get an ASR */
374 macnet_sendevent(sock, NE_DIED);
379 static void macnet_closed(TCPiopb* iopb) {
380 Socket *sock = (Socket *)iopb->csParam.close.userDataPtr;
383 olda5 = SetA5(sock->a5);
384 switch (iopb->ioResult) {
386 macnet_sendevent(sock, NE_CLOSED);
388 case connectionClosing:
389 case connectionTerminated:
392 macnet_sendevent(sock, NE_DIED);
399 * Free all the data structures associated with a socket and tear down
400 * any connection through it.
402 void net_destroy(Socket *sock) {
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.
411 memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
412 iopb.csCode = TCPRelease;
413 err = PBControlSync((ParmBlkPtr)&iopb);
414 sfree(iopb.csParam.create.rcvBuff);
418 static void macnet_sendevent(Socket *sock, Net_Event_Type type) {
421 ne = (NetEvent *)macnet_freeq.qHead;
423 Dequeue(&ne->qelem, &macnet_freeq);
426 Enqueue(&ne->qelem, &macnet_eventq);
427 WakeUpProcess(&sock->psn);
432 * c-file-style: "simon"