]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Yeah. Some progress on Mac OS networking. Still some way to go, though.
authorBen Harris <bjh21@bjh21.me.uk>
Sat, 3 Apr 1999 21:53:29 +0000 (21:53 +0000)
committerBen Harris <bjh21@bjh21.me.uk>
Sat, 3 Apr 1999 21:53:29 +0000 (21:53 +0000)
[originally from svn r155]

Makefile.mpw
macnet.c
macnet.h [deleted file]
putty.h

index 4cee8db8a53f22ad7716cd7770a78cb6a2c2b3a6..61d1c7c5f6516bf309c06eaa75c3a920aa7a8a39 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile.mpw,v 1.1.2.10 1999/04/02 12:56:57 ben Exp $
+# $Id: Makefile.mpw,v 1.1.2.11 1999/04/03 21:53:29 ben Exp $
 # This is the Makefile for building PuTTY for the Mac OS.
 # Users of non-Mac systems will see some pretty strange characters around.
 
@@ -120,6 +120,7 @@ PuTTY 
 
 mac.c.o        mac.c.x         Ä putty.h mac.h macresid.h
 maccfg.c.o     maccfg.c.x      Ä putty.h mac.h macresid.h
+macnet.c.o     macnet.c.x      Ä putty.h
 macterm.c.o    macterm.c.x     Ä putty.h mac.h
 misc.c.o       misc.c.x        Ä putty.h
 ssh.c.o                ssh.c.x         Ä putty.h ssh.h
index a4b7944b2023835c54a966fee82f54e6ad3f5477..059bb9e228ac266efd4a0e09aa39b92e9b6851e3 100644 (file)
--- a/macnet.c
+++ b/macnet.c
@@ -1,4 +1,4 @@
-/* $Id: macnet.c,v 1.1.2.1 1999/04/01 21:26:03 ben Exp $ */
+/* $Id: macnet.c,v 1.1.2.2 1999/04/03 21:53:29 ben Exp $ */
 /*
  * Copyright (c) 1999 Ben Harris
  * All rights reserved.
  */
 
 #include <MacTypes.h>
-#include <AddresXlation.h>
-#incldue <MacTCP.h>
+#include <AddressXlation.h>
+#include <Devices.h>
+#include <MacTCP.h>
 #include <MixedMode.h>
+#include <OSUtils.h>
 #include <Processes.h>
 
+#include <assert.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "putty.h"
 
+/*
+ * The theory behind this stuff:
+ *
+ * net_recv attempts to deliver any incoming data waiting on the
+ * queue.  Since MacTCP maintains a buffer for incoming data, there's
+ * no need for us to run asynchronous TCPRcvs, and we just do a
+ * synchronous one if we detect some data waiting.  Since TCPRcv can't
+ * be given a timeout of zero, we use TCPStatus to work out if there's
+ * anything waiting first.
+ *
+ * Sending data is trickier.  TCPSend reserves the right to block
+ * until everything we've sent is ACKed, which means we have to use it
+ * asynchronously.  In order to make life easier for backends, and to
+ * save a proliferation of circular buffers, we guarantee to take data
+ * off the hands of the backend as soon as it gives it to us.  This is
+ * reasonable because currently there's no way for the backend to say
+ * it can't take data, and once it's got them, it may as well give
+ * them to us.
+ * 
+ * Anyway, in order to avoid a fixed-size buffer overflowing, the send
+ * buffer is kept as a queue of blocks.  When net_send is called, we
+ * malloc a new block and stick it on the queue.  If the queue was
+ * empty, we kick off a new asynchronous TCPSend to handle our block.
+ *
+ */
+
+typedef struct Socket {
+    TCPiopb iopb; /* current MacTCP operation */
+    TCPiopb spareiopb; /* for closing etc */
+    hostInfo hostinfo;
+    int port;
+//    unsigned char *inbuf;
+//    int inbuf_head, inbuf_reap, inbuf_size;
+//    unsigned char *outbuf;
+//    int outbuf_head, outbuf_reap, outbuf_size;
+    ProcessSerialNumber psn;
+    Session *s;
+    UInt32 a5;
+    qHdr sendq; /* Blocks waiting to be sent */
+    qHdr freeq; /* Blocks sent, waiting to be freed */
+} Socket;
+
+typedef struct {
+    QElem qelem;
+    int flags;
+    int len;
+} Send_Buffer;
+
+/*
+ * Yes, I know the struct QElem has a short[1] to represent the user
+ * data.  I'm ignoring it because it makes my code prettier and
+ * improves the alignment.
+ */
+
+typedef struct {
+    QElem qelem;
+    Socket *sock;
+    Net_Event_Type type;
+} NetEvent;
+
+#define TCPBUF_SIZE 8192
+
+static QHdr macnet_eventq;
+static QHdr macnet_freeq;
+
 static short mtcp_refnum;
-statis OSErr mtcp_initted = FALSE;
+static int mtcp_initted = FALSE;
 
-static void macnet_init(void);
-static pascal void macnet_resolved(struct HostInfo *, char *);
+static OSErr macnet_init(void);
+static pascal void macnet_resolved(hostInfo *, char *);
+static void macnet_opened(TCPiopb*);
+static void macnet_sent(TCPiopb*);
+static void macnet_closed(TCPiopb*);
+static pascal void macnet_asr(StreamPtr, unsigned short, Ptr, unsigned short,
+                             ICMPReport *);
+static void macnet_sendevent(Socket *, Net_Event_Type);
 
-#ifdef TARGET_RT_MAC_CFM
+#if TARGET_RT_MAC_CFM
 static RoutineDescriptor macnet_resolved_upp =
     BUILD_ROUTINE_DESCRIPTOR(uppResultProcInfo, (ProcPtr)macnet_resolved);
+static RoutineDescriptor macnet_opened_upp =
+    BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
+                            (ProcPtr)macnet_opened);
+static RoutineDescriptor macnet_sent_upp =
+    BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo, (ProcPtr)macnet_sent);
+static RoutineDescriptor macnet_closed_upp =
+    BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo,
+                            (ProcPtr)macnet_closed);
+static RoutineDescriptor macnet_asr_upp =
+    BUILD_ROUTINE_DESCRIPTOR(uppTCPNotifyProcInfo, (ProcPtr)macnet_asr);
 #else
 #define macnet_resolved_upp macnet_resolved
+#define macnet_opened_upp macnet_opened
+#define macnet_sent_upp macnet_sent
+#define macnet_closed_upp macnet_closed
+#define macnet_asr_upp macnet_asr
 #endif
 
+/*
+ * Number of outstanding network events allowed. 
+ */
+#define NUM_EVENTS 16
+
 /*
  * Initialise networking.  Set mtcp_initted if it goes OK.
  */
 static OSErr macnet_init(void) {
     OSErr err;
+    NetEvent *eventblock;
+    int i;
 
-    err = OpenDriver(".IPP", &mtcp_refnum);
+    err = opendriver(".IPP", &mtcp_refnum);
     if (err != noErr)
        return err;
     err = OpenResolver(NULL);
     if (err != noErr)
        return err;
+    /* Set up the event queues, and fill the free queue with events */
+    macnet_eventq.qFlags = 0;
+    macnet_eventq.qHead = macnet_eventq.qTail = NULL;
+    macnet_freeq.qFlags = 0;
+    macnet_freeq.qHead = macnet_eventq.qTail = NULL;
+    eventblock = smalloc(NUM_EVENTS * sizeof(NetEvent));
+    for (i = 0; i < NUM_EVENTS; i++)
+       Enqueue(&eventblock[i].qelem, &macnet_freeq);
     mtcp_initted = TRUE;
-
-    /* XXX: otherwise report an error */
 }
 
-Socket *tcp_open(const char *host, int port, char **realhost) {
+Socket *net_open(Session *s, char *host, int port) {
     ip_addr a;
-    OSError err = noErr;
-    Socket *s;
+    OSErr err = noErr;
+    Socket *sock;
+    void *tcpbuf;
 
-    s = smalloc(sizeof(struct Socket));
+    /*
+     * First, get hold of all the memory we'll need (a lot of the
+     * later stuff happens at interrupt time)
+     */
+    sock = smalloc(sizeof(struct Socket));
+    memset(sock, 0, sizeof(*sock));
+    tcpbuf = smalloc(TCPBUF_SIZE);
+
+    /* Make a note of anything we don't want to forget */
+    sock->port = port;
+    GetCurrentProcess(&sock->psn);
+    sock->a5 = SetCurrentA5();
+
+    /* Get MacTCP running if it's not already */
     if (!mtcp_initted)
        if ((err = macnet_init()) != noErr)
            fatalbox("Couldn't init network (%d)", err);
-    s->port = port;
-    GetCurrentProcess(&s->psn);
-    err = StrToAddr(host, &s->host_info, &macnet_resolved_upp, (char *)s);
+
+    /* Get ourselves a TCP stream to play with */
+    sock->iopb.ioCRefNum = mtcp_refnum;
+    sock->iopb.csCode = TCPCreate;
+    sock->iopb.csParam.create.rcvBuff = tcpbuf;
+    sock->iopb.csParam.create.rcvBuffLen = TCPBUF_SIZE;
+    sock->iopb.csParam.create.notifyProc = macnet_asr_upp;
+    sock->iopb.csParam.create.userDataPtr = (Ptr)sock;
+    /* This could be done asynchronously, but I doubt it'll take long. */
+    err = PBControlSync((ParmBlkPtr)&sock->iopb);
+    if (err != noErr)
+       fatalbox("TCP stream open failed (%d)", err);
+
+    err = StrToAddr(host, &sock->hostinfo, &macnet_resolved_upp, (char *)sock);
     if (err != noErr)
        fatalbox("Host lookup failed (%d)", err);
-    if (s->host_info.rtnCode != cacheFault)
-       macnet_resolved(&s->host_info, s);
-    return s;
+    if (sock->hostinfo.rtnCode != cacheFault)
+       macnet_resolved(&sock->hostinfo, (char *)sock);
+    return sock;
+}
+
+static pascal void macnet_resolved(hostInfo *hi, char *cookie) {
+    Socket *sock = (Socket *)cookie;
+    OSErr err;
+    UInt32 olda5;
+
+    olda5 = SetA5(sock->a5);
+    /*
+     * We've resolved a name, so now we'd like to connect to it (or
+     * report an error).
+     */
+    switch (sock->hostinfo.rtnCode) {
+      case noErr:
+       /* Open a connection */
+       sock->iopb.ioCompletion = macnet_opened_upp;
+       sock->iopb.csCode = TCPActiveOpen;
+       sock->iopb.csParam.open.validityFlags = typeOfService;
+       sock->iopb.csParam.open.commandTimeoutValue = 0; /* unused */
+       sock->iopb.csParam.open.remoteHost = sock->hostinfo.addr[0]; /*XXX*/
+       sock->iopb.csParam.open.remotePort = sock->port;
+       /* localHost is set by MacTCP. */
+       sock->iopb.csParam.open.localPort = 0;
+       sock->iopb.csParam.open.tosFlags = lowDelay;
+       sock->iopb.csParam.open.dontFrag = 0;
+       sock->iopb.csParam.open.timeToLive = 0; /* default */
+       sock->iopb.csParam.open.security = 0;
+       sock->iopb.csParam.open.optionCnt = 0;
+       sock->iopb.csParam.open.userDataPtr = (char *)sock;
+       err = PBControlSync((ParmBlkPtr)&sock->iopb);
+       if (err != noErr)
+           macnet_sendevent(sock, NE_NOOPEN);
+       break;
+      default: /* Something went wrong */
+       macnet_sendevent(sock, NE_NOHOST);
+       break;
+    }
+    SetA5(olda5);
+}
+
+static void macnet_opened(TCPiopb *iopb) {
+    Socket *sock = (Socket *)iopb->csParam.open.userDataPtr;
+    UInt32 olda5;
+
+    olda5 = SetA5(sock->a5);
+    switch (iopb->ioResult) {
+      case noErr:
+       macnet_sendevent(sock, NE_OPEN);
+       break;
+      default:
+       macnet_sendevent(sock, NE_NOOPEN);
+       break;
+    }
+    SetA5(olda5);
+}
+
+static pascal void macnet_asr(StreamPtr tcpstream, unsigned short eventcode,
+                             Ptr cookie, unsigned short terminreason,
+                             ICMPReport *icmpmsg) {
+    Socket *sock = (Socket *)cookie;
+    UInt32 olda5;
+
+    olda5 = SetA5(sock->a5);
+    switch (eventcode) {
+      case TCPClosing:
+       macnet_sendevent(sock, NE_CLOSING);
+       break;
+      case TCPULPTimeout:
+       macnet_sendevent(sock, NE_TIMEOUT);
+       break;
+      case TCPTerminate:
+       switch (terminreason) {
+         case TCPRemoteAbort:
+           macnet_sendevent(sock, NE_ABORT);
+           break;
+         default:
+           macnet_sendevent(sock, NE_DIED);
+           break;
+       }
+       break;
+      case TCPDataArrival:
+       macnet_sendevent(sock, NE_DATA);
+       break;
+      case TCPUrgent:
+       macnet_sendevent(sock, NE_URGENT);
+       break;
+      case TCPICMPReceived:
+       switch (icmpmsg->reportType) {
+         case portUnreach:
+           macnet_sendevent(sock, NE_REFUSED);
+           break;
+       }
+       break;
+    }
+    SetA5(olda5);
+}
+
+/*
+ * Send a block of data.
+ */
+
+int net_send(Socket *sock, void *buf, int buflen, int flags) {{
+    OSErr err;
+    Send_Buffer *buff;
+
+    buff = smalloc(sizeof(Send_Buffer) + buflen);
+    buff->flags = flags;
+    buff->len = buflen;
+    memcpy(buff + 1, buf, buflen);
+    Enqueue(&buff->qelem, &sock->sendq);
+    macnet_start(sock);
+}
+
+int net_recv(Socket *sock, void *buf, int buflen, int flags) {
+    TCPiopb iopb;
+    OSErr err;
+    int avail, want, got;
+
+    memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
+    /* Work out if there's anything to recieve (we don't want to block) */
+    iopb.csCode = TCPStatus;
+    err = PBControlSync((ParmBlkPtr)&iopb);
+    if (err != noErr)
+       return 0; /* macnet_asr should catch it anyway */
+    avail = iopb.csParam.status.amtUnreadData;
+    if (avail == 0)
+       return 0;
+    want = avail < buflen ? avail : buflen;
+    iopb.csCode = TCPRcv;
+    iopb.csParam.receive.buffPtr = buf;
+    iopb.csParam.receive.buffLen = want;
+    err = PBControlSync((ParmBlkPtr)&iopb);
+    if (err != noErr)
+       return 0;
+    return iopb.csParam.receive.buffLen;
+}
+       
+
+void net_close(Socket *sock) {
+    OSErr err;
+
+    /*
+     * This might get called in the middle of processing another
+     * request on the socket, so we have a spare parameter block for
+     * this purpose (allocating one dynamically would mean having to
+     * free it, which we can't do at interrupt time).
+     */
+    memcpy(&sock->spareiopb, &sock->iopb, sizeof(TCPiopb));
+    sock->spareiopb.ioCompletion = macnet_closed_upp;
+    sock->spareiopb.csCode = TCPClose;
+    sock->spareiopb.csParam.close.validityFlags = 0;
+    sock->spareiopb.csParam.close.userDataPtr = (char *)sock;
+    err = PBControlAsync((ParmBlkPtr)&sock->spareiopb);
+    switch (err) {
+      case noErr:
+      case connectionClosing:
+      case connectionTerminated: /* We'll get an ASR */
+       break;
+      default:
+       macnet_sendevent(sock, NE_DIED);
+       break;
+    }
+}
+
+static void macnet_closed(TCPiopb* iopb) {
+    Socket *sock = (Socket *)iopb->csParam.close.userDataPtr;
+    UInt32 olda5;
+
+    olda5 = SetA5(sock->a5);
+    switch (iopb->ioResult) {
+      case noErr:
+       macnet_sendevent(sock, NE_CLOSED);
+       break;
+      case connectionClosing:
+      case connectionTerminated:
+       break;
+      default:
+       macnet_sendevent(sock, NE_DIED);
+       break;
+    }
+    SetA5(olda5);
+}
+
+/*
+ * Free all the data structures associated with a socket and tear down
+ * any connection through it.
+ */
+void net_destroy(Socket *sock) {
+    TCPiopb iopb;
+    OSErr err;
+
+    /*
+     * Yes, we need _another_ iopb, as there may be a send _and_ a
+     * close outstanding.  Luckily, destroying a socket is
+     * synchronous, so we can allocate this one dynamically.
+     */
+    memcpy(&iopb, &sock->iopb, sizeof(TCPiopb));
+    iopb.csCode = TCPRelease;
+    err = PBControlSync((ParmBlkPtr)&iopb);
+    sfree(iopb.csParam.create.rcvBuff);
+    sfree(sock);
 }
 
-static pascal void macnet_resolved(struct hostInfo *hi, char *cookie) {
-    Socket *s = (Socket *)cookie;
+static void macnet_sendevent(Socket *sock, Net_Event_Type type) {
+    NetEvent *ne;
 
-    /* We should probably tell the process what's going on here. */
-    /* Alternatively, we should kick off the next stage in the process */
-    WakeUpProcess(&s->psn);
+    ne = (NetEvent *)macnet_freeq.qHead;
+    assert (ne != NULL);
+    Dequeue(&ne->qelem, &macnet_freeq);
+    ne->sock = sock;
+    ne->type = type;
+    Enqueue(&ne->qelem, &macnet_eventq);
+    WakeUpProcess(&sock->psn);
 }
 
 /*
diff --git a/macnet.h b/macnet.h
deleted file mode 100644 (file)
index 74cd6d1..0000000
--- a/macnet.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * macnet.h -- Mac OS networtking stuff for PuTTY
- */
-
-#ifndef _PUTTY_MACNET_H
-#define _PUTTY_MACNET_H
-
-#include <MacTypes.h>
-#include <AddressXlation.h>
-#include <MacTCP.h>
-#include <Processes.h>
-
-typedef struct {
-    StreamPtr tcp_stream;
-    struct HostInfo host_info;
-    int port;
-    unsigned char *inbuf;
-    int inbuf_head, inbuf_reap, inbuf_size;
-    unsigned char *outbuf;
-    int outbuf_head, outbuf_reap, outbuf_size;
-    ProcessSerialNumber psn;
-} Socket;
-
-typedef Socket *SOCKET
-
-#define INVALID_SOCKET NULL
-
-#define MSG_OOB 1
-
-extern int send(SOCKET, const void *, size_t, int);
-extern int recv(SOCKET, void *, size_t, int);
-extern SOCKET tcp_open(const char *, int, char **);
-extern void tcp_close(SOCKET);
-extern void tcp_abort(SOCKET);
-
-#endif
-
-/*
- * Local Variables:
- * c-file-style: "simon"
- * End:
- */
diff --git a/putty.h b/putty.h
index 87f05d2f2a02e98334ae29a9414cd28d3175157e..3681de53965b25c0a92d81daffcb653304a51902 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -90,18 +90,33 @@ typedef enum {
 } VT_Mode;
 
 typedef struct Session Session;
+typedef struct Socket Socket;
+
+/* Types of network event */
+
+typedef enum {
+    NE_NULL,   /* Nothing happened */
+    NE_OPEN,   /* Connection successfully opened */
+    NE_NOHOST, /* DNS lookup failed for some reason */
+    NE_REFUSED,        /* Port unreachable */
+    NE_NOOPEN, /* Connection failed to open for some other reason */
+    NE_DATA,   /* Incoming normal data */
+    NE_URGENT, /* Incoming urgent data */
+    NE_CLOSING,        /* Connection closed by remote host */
+    NE_CLOSED, /* Connection close completed */
+    NE_TIMEOUT,        /* Remote host vanished */
+    NE_ABORT,  /* Remote host reset connection */
+    NE_DIED,   /* Connection has failed for some other reason */
+} Net_Event_Type;
+
 
 typedef struct {
-#ifdef macintosh
-       char *(*init) (Session *, char *host, int port, char **realhost);
-       int (*msg)(Session *);
-#else /* not macintosh */
-    char *(*init) (HWND hwnd, char *host, int port, char **realhost);
-    int (*msg) (WPARAM wParam, LPARAM lParam);
-#endif /* not macintosh */
+    char *(*init) (Session *, char *host, int port);
+    int (*msg)(Session *, Socket *, Net_Event_Type);
     void (*send) (Session *, char *buf, int len);
     void (*size) (Session *);
     void (*special) (Session *, Telnet_Special code);
+    void (*shutdown) (Session *);
 } Backend;
 
 typedef struct {
@@ -252,6 +267,7 @@ typedef struct Session {
 #endif
 } Session;
 
+typedef struct Socket Socket;
 
 /*
  * Exports from display system
@@ -273,7 +289,17 @@ void fatalbox (const char *, ...);
 #pragma noreturn (fatalbox)
 #endif
 extern void beep (Session *s);
-#define OPTIMISE_IS_SCROLL 1
+
+/*
+ * Exports from the network system
+ */
+
+extern Socket *net_open(Session *, char *host, int port);
+extern char *net_realname(Socket *);
+extern int net_recv(Socket *, void *, int, int);
+extern int net_send(Socket *, void *, int, int);
+extern void net_close(Socket *); /* ask the remote end to close */
+extern void net_destroy(Socket *); /* Tidy up */
 
 /*
  * Exports from noise.c.