Dave Hinton's modifications to the network layer interface, which
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Tue, 13 Mar 2001 10:22:45 +0000 (10:22 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Tue, 13 Mar 2001 10:22:45 +0000 (10:22 +0000)
should make it possible to add SSL support later.

git-svn-id: svn://svn.tartarus.org/sgt/putty@996 cda61777-01e9-0310-a592-d414129be87e

network.h
raw.c
rlogin.c
ssh.c
telnet.c
winnet.c
x11fwd.c

index ba1e1df..f8b81a3 100644 (file)
--- a/network.h
+++ b/network.h
@@ -3,52 +3,78 @@
  *
  * The way this works is: a back end can choose to open any number
  * of sockets - including zero, which might be necessary in some.
- * It can register a function to be called when data comes in on
- * any given one, and it can call the networking abstraction to
- * send data without having to worry about blocking. The stuff
- * behind the abstraction takes care of selects and nonblocking
- * writes and all that sort of painful gubbins.
+ * It can register a bunch of callbacks (most notably for when 
+ * data is received) for each socket, and it can call the networking
+ * abstraction to send data without having to worry about blocking.
+ * The stuff behind the abstraction takes care of selects and
+ * nonblocking writes and all that sort of painful gubbins.
  */
 
 #ifndef PUTTY_NETWORK_H
 #define PUTTY_NETWORK_H
 
-typedef struct Socket_tag *Socket;
 typedef struct SockAddr_tag *SockAddr;
+/* pay attention to levels of indirection */
+typedef struct socket_function_table **Socket;
+typedef struct plug_function_table **Plug;
+
+struct socket_function_table {
+    Plug (*plug) (Socket s, Plug p);
+       /* use a different plug (return the old one) */
+       /* if p is NULL, it doesn't change the plug */
+       /* but it does return the one it's using */
+    void (*close) (Socket s);
+    void (*write) (Socket s, char *data, int len);
+    void (*write_oob) (Socket s, char *data, int len);
+    void (*flush) (Socket s);
+       /* ignored by tcp, but vital for ssl */
+    char *(*socket_error) (Socket s);
+};
+
+struct plug_function_table {
+    int (*closing)
+       (Plug p, char *error_msg, int error_code, int calling_back);
+       /* error_msg is NULL iff it is not an error (ie it closed normally) */
+       /* calling_back != 0 iff there is a Plug function */
+       /* currently running (would cure the fixme in try_send()) */
+    int (*receive) (Plug p, int urgent, char *data, int len);
+       /*
+        *  - urgent==0. `data' points to `len' bytes of perfectly
+        *    ordinary data.
+        * 
+        *  - urgent==1. `data' points to `len' bytes of data,
+        *    which were read from before an Urgent pointer.
+        * 
+        *  - urgent==2. `data' points to `len' bytes of data,
+        *    the first of which was the one at the Urgent mark.
+        */
+};
 
-/*
- * This is the function a client must register with each socket, to
- * receive data coming in on that socket. The parameter `urgent'
- * decides the meaning of `data' and `len':
- * 
- *  - urgent==0. `data' points to `len' bytes of perfectly ordinary
- *    data.
- * 
- *  - urgent==1. `data' points to `len' bytes of data, which were
- *    read from before an Urgent pointer.
- * 
- *  - urgent==2. `data' points to `len' bytes of data, the first of
- *    which was the one at the Urgent mark.
- * 
- *  - urgent==3. An error has occurred on the socket. `data' points
- *    to an error string, and `len' points to an error code.
- */
-typedef int (*sk_receiver_t)(Socket s, int urgent, char *data, int len);
 
 void sk_init(void);                   /* called once at program startup */
 
 SockAddr sk_namelookup(char *host, char **canonicalname);
 void sk_addr_free(SockAddr addr);
 
-Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
-              sk_receiver_t receiver);
-void sk_close(Socket s);
-void sk_write(Socket s, char *buf, int len);
-void sk_write_oob(Socket s, char *buf, int len);
+Socket sk_new(SockAddr addr, int port, int privport, int oobinline, Plug p);
+
+#define sk_plug(s,p) (((*s)->plug) (s, p))
+#define sk_close(s) (((*s)->close) (s))
+#define sk_write(s,buf,len) (((*s)->write) (s, buf, len))
+#define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len))
+#define sk_flush(s) (((*s)->flush) (s))
+
+#ifdef DEFINE_PLUG_METHOD_MACROS
+#define plug_closing(p,msg,code,callback) (((*p)->closing) (p, msg, code, callback))
+#define plug_receive(p,urgent,buf,len) (((*p)->receive) (p, urgent, buf, len))
+#endif
 
 /*
  * Each socket abstraction contains a `void *' private field in
  * which the client can keep state.
+ *
+ * This is perhaps unnecessary now that we have the notion of a plug,
+ * but there is some existing code that uses it, so it stays.
  */
 void sk_set_private_ptr(Socket s, void *ptr);
 void *sk_get_private_ptr(Socket s);
@@ -59,6 +85,46 @@ void *sk_get_private_ptr(Socket s);
  * or return NULL if there's no problem.
  */
 char *sk_addr_error(SockAddr addr);
-char *sk_socket_error(Socket addr);
+#define sk_socket_error(s) (((*s)->socket_error) (s))
+
+
+/********** SSL stuff **********/
+
+/*
+ * This section is subject to change, but you get the general idea
+ * of what it will eventually look like.
+ */
+
+
+typedef struct certificate *Certificate;
+typedef struct our_certificate *Our_Certificate;
+    /* to be defined somewhere else, somehow */
+
+typedef struct ssl_client_socket_function_table **SSL_Client_Socket;
+typedef struct ssl_client_plug_function_table **SSL_Client_Plug;
+
+struct ssl_client_socket_function_table {
+    struct socket_function_table base;
+    void (*renegotiate) (SSL_Client_Socket s);
+       /* renegotiate the cipher spec */
+};
+
+struct ssl_client_plug_function_table {
+    struct plug_function_table base;
+    int (*refuse_cert) (SSL_Client_Plug p, Certificate cert[]);
+       /* do we accept this certificate chain?  If not, why not? */
+       /* cert[0] is the server's certificate, cert[] is NULL-terminated */
+       /* the last certificate may or may not be the root certificate */
+    Our_Certificate (*client_cert) (SSL_Client_Plug p);
+       /* the server wants us to identify ourselves */
+       /* may return NULL if we want anonymity */
+};
+
+SSL_Client_Socket sk_ssl_client_over (
+    Socket s, /* pre-existing (tcp) connection */
+    SSL_Client_Plug p
+);
+
+#define sk_renegotiate(s) (((*s)->renegotiate) (s))
 
 #endif
diff --git a/raw.c b/raw.c
index e3d5c06..7990d1d 100644 (file)
--- a/raw.c
+++ b/raw.c
@@ -24,20 +24,17 @@ static void c_write (char *buf, int len) {
     from_backend(0, buf, len);
 }
 
-static int raw_receive (Socket skt, int urgent, char *data, int len) {
-    if (urgent==3) {
+static int raw_closing (Plug plug, char *error_msg, int error_code, int calling_back) {
+    sk_close(s);
+    s = NULL;
+    if (error_msg) {
         /* A socket error has occurred. */
-        sk_close(s);
-        s = NULL;
-        connection_fatal(data);
-        len = 0;
-        return 0;
-    } else if (!len) {
-       /* Connection has closed. */
-       sk_close(s);
-       s = NULL;
-       return 0;
-    }
+        connection_fatal (error_msg);
+    } /* Otherwise, the remote side closed the connection normally. */
+    return 0;
+}
+
+static int raw_receive (Plug plug, int urgent, char *data, int len) {
     c_write(data, len);
     return 1;
 }
@@ -50,6 +47,11 @@ static int raw_receive (Socket skt, int urgent, char *data, int len) {
  * Also places the canonical host name into `realhost'.
  */
 static char *raw_init (char *host, int port, char **realhost) {
+    static struct plug_function_table fn_table = {
+       raw_closing,
+       raw_receive
+    }, *fn_table_ptr = &fn_table;
+
     SockAddr addr;
     char *err;
 
@@ -66,7 +68,7 @@ static char *raw_init (char *host, int port, char **realhost) {
     /*
      * Open socket.
      */
-    s = sk_new(addr, port, 0, 1, raw_receive);
+    s = sk_new(addr, port, 0, 1, &fn_table_ptr);
     if ( (err = sk_socket_error(s)) )
        return err;
 
index eb6a6c2..acb27c8 100644 (file)
--- a/rlogin.c
+++ b/rlogin.c
@@ -25,19 +25,17 @@ static void c_write (char *buf, int len) {
     from_backend(0, buf, len);
 }
 
-static int rlogin_receive (Socket skt, int urgent, char *data, int len) {
-    if (urgent==3) {
+static int rlogin_closing (Plug plug, char *error_msg, int error_code, int calling_back) {
+    sk_close(s);
+    s = NULL;
+    if (error_msg) {
         /* A socket error has occurred. */
-        sk_close(s);
-        s = NULL;
-        connection_fatal(data);
-        return 0;
-    } else if (!len) {
-       /* Connection has closed. */
-       sk_close(s);
-       s = NULL;
-       return 0;
-    }
+        connection_fatal (error_msg);
+    } /* Otherwise, the remote side closed the connection normally. */
+    return 0;
+}
+
+static int rlogin_receive (Plug plug, int urgent, char *data, int len) {
     if (urgent == 2) {
         char c;
         
@@ -77,6 +75,11 @@ static int rlogin_receive (Socket skt, int urgent, char *data, int len) {
  * Also places the canonical host name into `realhost'.
  */
 static char *rlogin_init (char *host, int port, char **realhost) {
+    static struct plug_function_table fn_table = {
+       rlogin_closing,
+       rlogin_receive
+    }, *fn_table_ptr = &fn_table;
+
     SockAddr addr;
     char *err;
 
@@ -93,7 +96,7 @@ static char *rlogin_init (char *host, int port, char **realhost) {
     /*
      * Open socket.
      */
-    s = sk_new(addr, port, 1, 0, rlogin_receive);
+    s = sk_new(addr, port, 1, 0, &fn_table_ptr);
     if ( (err = sk_socket_error(s)) )
        return err;
 
diff --git a/ssh.c b/ssh.c
index 549a1e5..3af4ba9 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -1270,21 +1270,20 @@ static void ssh_gotdata(unsigned char *data, int datalen)
     crFinishV;
 }
 
-static int ssh_receive(Socket skt, int urgent, char *data, int len) {
-    if (urgent==3) {
+static int ssh_closing (Plug plug, char *error_msg, int error_code, int calling_back) {
+    ssh_state = SSH_STATE_CLOSED;
+    sk_close(s);
+    s = NULL;
+    if (error_msg) {
         /* A socket error has occurred. */
-        ssh_state = SSH_STATE_CLOSED;
-        sk_close(s);
-        s = NULL;
-        connection_fatal(data);
-        return 0;
-    } else if (!len) {
-       /* Connection has closed. */
-       ssh_state = SSH_STATE_CLOSED;
-       sk_close(s);
-       s = NULL;
-       return 0;
+        connection_fatal (error_msg);
+    } else {
+       /* Otherwise, the remote side closed the connection normally. */
     }
+    return 0;
+}
+
+static int ssh_receive(Plug plug, int urgent, char *data, int len) {
     ssh_gotdata (data, len);
     if (ssh_state == SSH_STATE_CLOSED) {
         if (s) {
@@ -1303,6 +1302,11 @@ static int ssh_receive(Socket skt, int urgent, char *data, int len) {
  */
 static char *connect_to_host(char *host, int port, char **realhost)
 {
+    static struct plug_function_table fn_table = {
+       ssh_closing,
+       ssh_receive
+    }, *fn_table_ptr = &fn_table;
+
     SockAddr addr;
     char *err;
 #ifdef FWHACK
@@ -1340,7 +1344,7 @@ static char *connect_to_host(char *host, int port, char **realhost)
     /*
      * Open socket.
      */
-    s = sk_new(addr, port, 0, 1, ssh_receive);
+    s = sk_new(addr, port, 0, 1, &fn_table_ptr);
     if ( (err = sk_socket_error(s)) )
        return err;
 
index 6595342..29daf0f 100644 (file)
--- a/telnet.c
+++ b/telnet.c
@@ -465,19 +465,17 @@ static void do_telnet_read (char *buf, int len) {
     }
 }
 
-static int telnet_receive(Socket skt, int urgent, char *data, int len) {
-    if (urgent==3) {
+static int telnet_closing (Plug plug, char *error_msg, int error_code, int calling_back) {
+    sk_close(s);
+    s = NULL;
+    if (error_msg) {
         /* A socket error has occurred. */
-        sk_close(s);
-        s = NULL;
-        connection_fatal(data);
-        return 0;
-    } else if (!len) {
-       /* Connection has closed. */
-       sk_close(s);
-       s = NULL;
-       return 0;
-    }
+        connection_fatal (error_msg);
+    } /* Otherwise, the remote side closed the connection normally. */
+    return 0;
+}
+
+static int telnet_receive(Plug plug, int urgent, char *data, int len) {
     if(urgent) in_synch = TRUE;
     do_telnet_read (data, len);
     return 1;
@@ -491,6 +489,11 @@ static int telnet_receive(Socket skt, int urgent, char *data, int len) {
  * Also places the canonical host name into `realhost'.
  */
 static char *telnet_init (char *host, int port, char **realhost) {
+    static struct plug_function_table fn_table = {
+       telnet_closing,
+       telnet_receive
+    }, *fn_table_ptr = &fn_table;
+
     SockAddr addr;
     char *err;
 
@@ -507,7 +510,7 @@ static char *telnet_init (char *host, int port, char **realhost) {
     /*
      * Open socket.
      */
-    s = sk_new(addr, port, 0, 1, telnet_receive);
+    s = sk_new(addr, port, 0, 1, &fn_table_ptr);
     if ( (err = sk_socket_error(s)) )
        return err;
 
index f15b136..01b4f1b 100644 (file)
--- a/winnet.c
+++ b/winnet.c
@@ -49,6 +49,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#define DEFINE_PLUG_METHOD_MACROS
 #include "putty.h"
 #include "network.h"
 #include "tree234.h"
 #define BUFFER_GRANULE 512
 
 struct Socket_tag {
+    struct socket_function_table *fn;
+    /* the above variable absolutely *must* be the first in this structure */
     char *error;
     SOCKET s;
-    sk_receiver_t receiver;
+    Plug plug;
     void *private_ptr;
     struct buffer *head, *tail;
     int writable;
@@ -66,6 +69,16 @@ struct Socket_tag {
     int oobinline;
 };
 
+/*
+ * We used to typedef struct Socket_tag *Socket.
+ *
+ * Since we have made the networking abstraction slightly more
+ * abstract, Socket no longer means a tcp socket (it could mean
+ * an ssl socket).  So now we must use Actual_Socket when we know
+ * we are talking about a tcp socket.
+ */
+typedef struct Socket_tag *Actual_Socket;
+
 struct SockAddr_tag {
     char *error;
     /* address family this belongs to, AF_INET for IPv4, AF_INET6 for IPv6. */
@@ -90,7 +103,7 @@ struct buffer {
 static tree234 *sktree;
 
 static int cmpfortree(void *av, void *bv) {
-    Socket a = (Socket)av, b = (Socket)bv;
+    Actual_Socket a = (Actual_Socket)av, b = (Actual_Socket)bv;
     unsigned long as = (unsigned long)a->s, bs = (unsigned long)b->s;
     if (as < bs) return -1;
     if (as > bs) return +1;
@@ -98,7 +111,7 @@ static int cmpfortree(void *av, void *bv) {
 }
 
 static int cmpforsearch(void *av, void *bv) {
-    Socket b = (Socket)bv;
+    Actual_Socket b = (Actual_Socket)bv;
     unsigned long as = (unsigned long)av, bs = (unsigned long)b->s;
     if (as < bs) return -1;
     if (as > bs) return +1;
@@ -289,8 +302,37 @@ void sk_addr_free(SockAddr addr) {
     sfree(addr);
 }
 
+static Plug sk_tcp_plug (Socket sock, Plug p) {
+    Actual_Socket s = (Actual_Socket) sock;
+    Plug ret = s->plug;
+    if (p) s->plug = p;
+    return ret;
+}
+
+static void sk_tcp_flush (Socket s) {
+    /*
+     * We send data to the socket as soon as we can anyway,
+     * so we don't need to do anything here.  :-)
+     */
+}
+
+void sk_tcp_close (Socket s);
+void sk_tcp_write (Socket s, char *data, int len);
+void sk_tcp_write_oob (Socket s, char *data, int len);
+char *sk_tcp_socket_error(Socket s);
+
 Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
-              sk_receiver_t receiver) {
+              Plug plug)
+{
+    static struct socket_function_table fn_table = {
+       sk_tcp_plug,
+       sk_tcp_close,
+       sk_tcp_write,
+       sk_tcp_write_oob,
+       sk_tcp_flush,
+       sk_tcp_socket_error
+    };
+
     SOCKET s;
 #ifdef IPV6
     SOCKADDR_IN6 a6;
@@ -298,7 +340,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     SOCKADDR_IN a;
     DWORD err;
     char *errstr;
-    Socket ret;
+    Actual_Socket ret;
     extern char *do_select(SOCKET skt, int startup);
     short localport;
 
@@ -306,8 +348,9 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
      * Create Socket structure.
      */
     ret = smalloc(sizeof(struct Socket_tag));
+    ret->fn = &fn_table;
     ret->error = NULL;
-    ret->receiver = receiver;
+    ret->plug = plug;
     ret->head = ret->tail = NULL;
     ret->writable = 1;                /* to start with */
     ret->sending_oob = 0;
@@ -321,7 +364,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     if (s == INVALID_SOCKET) {
        err = WSAGetLastError();
         ret->error = winsock_error_string(err);
-       return ret;
+       return (Socket) ret;
     }
 
     ret->oobinline = oobinline;
@@ -384,7 +427,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     if (err)
     {
        ret->error = winsock_error_string(err);
-       return ret;
+       return (Socket) ret;
     }
 
     /*
@@ -413,7 +456,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     {
        err = WSAGetLastError();
        ret->error = winsock_error_string(err);
-       return ret;
+       return (Socket) ret;
     }
 
     /* Set up a select mechanism. This could be an AsyncSelect on a
@@ -421,16 +464,17 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     errstr = do_select(s, 1);
     if (errstr) {
        ret->error = errstr;
-       return ret;
+       return (Socket) ret;
     }
 
     add234(sktree, ret);
 
-    return ret;
+    return (Socket) ret;
 }
 
-void sk_close(Socket s) {
+static void sk_tcp_close(Socket sock) {
     extern char *do_select(SOCKET skt, int startup);
+    Actual_Socket s = (Actual_Socket) sock;
 
     del234(sktree, s);
     do_select(s->s, 0);
@@ -442,7 +486,7 @@ void sk_close(Socket s) {
  * The function which tries to send on a socket once it's deemed
  * writable.
  */
-void try_send(Socket s) {
+void try_send(Actual_Socket s) {
     while (s->head) {
        int nsent;
        DWORD err;
@@ -497,7 +541,9 @@ void try_send(Socket s) {
     }
 }
 
-void sk_write(Socket s, char *buf, int len) {
+static void sk_tcp_write(Socket sock, char *buf, int len) {
+    Actual_Socket s = (Actual_Socket) sock;
+
     /*
      * Add the data to the buffer list on the socket.
      */
@@ -532,7 +578,9 @@ void sk_write(Socket s, char *buf, int len) {
        try_send(s);
 }
 
-void sk_write_oob(Socket s, char *buf, int len) {
+static void sk_tcp_write_oob(Socket sock, char *buf, int len) {
+    Actual_Socket s = (Actual_Socket) sock;
+
     /*
      * Replace the buffer list on the socket with the data.
      */
@@ -567,7 +615,7 @@ int select_result(WPARAM wParam, LPARAM lParam) {
     int ret, open;
     DWORD err;
     char buf[BUFFER_GRANULE];
-    Socket s;
+    Actual_Socket s;
     u_long atmark;
 
     /* wParam is the socket itself */
@@ -578,9 +626,9 @@ int select_result(WPARAM wParam, LPARAM lParam) {
     if ((err = WSAGETSELECTERROR(lParam)) != 0) {
         /*
          * An error has occurred on this socket. Pass it to the
-         * receiver function.
+         * plug.
          */
-        return s->receiver(s, 3, winsock_error_string(err), err);
+        return plug_closing (s->plug, winsock_error_string(err), err, 0);
     }
 
     noise_ultralight(lParam);
@@ -615,9 +663,11 @@ int select_result(WPARAM wParam, LPARAM lParam) {
            }
        }
        if (ret < 0) {
-           return s->receiver(s, 3, winsock_error_string(err), err);
+           return plug_closing (s->plug, winsock_error_string(err), err, 0);
+       } else if (0 == ret) {
+           return plug_closing (s->plug, NULL, 0, 0);
        } else {
-           return s->receiver(s, atmark ? 0 : 1, buf, ret);
+           return plug_receive (s->plug, atmark ? 0 : 1, buf, ret);
        }
        break;
       case FD_OOB:
@@ -633,7 +683,7 @@ int select_result(WPARAM wParam, LPARAM lParam) {
             fatalbox(ret == 0 ? "Internal networking trouble" :
                      winsock_error_string(WSAGetLastError()));
         } else {
-            return s->receiver(s, 2, buf, ret);
+           return plug_receive (s->plug, 2, buf, ret);
         }
         break;
       case FD_WRITE:
@@ -649,9 +699,11 @@ int select_result(WPARAM wParam, LPARAM lParam) {
                 err = WSAGetLastError();
                 if (err == WSAEWOULDBLOCK)
                     break;
-                return s->receiver(s, 3, winsock_error_string(err), err);
-            } else
-                open &= s->receiver(s, 0, buf, ret);
+                return plug_closing (s->plug, winsock_error_string(err), err, 0);
+            } else {
+               if (ret) open &= plug_receive (s->plug, 0, buf, ret);
+               else open &= plug_closing (s->plug, NULL, 0, 0);
+           }
        } while (ret > 0);
         return open;
     }
@@ -663,10 +715,12 @@ int select_result(WPARAM wParam, LPARAM lParam) {
  * Each socket abstraction contains a `void *' private field in
  * which the client can keep state.
  */
-void sk_set_private_ptr(Socket s, void *ptr) {
+void sk_set_private_ptr(Socket sock, void *ptr) {
+    Actual_Socket s = (Actual_Socket) sock;
     s->private_ptr = ptr;
 }
-void *sk_get_private_ptr(Socket s) {
+void *sk_get_private_ptr(Socket sock) {
+    Actual_Socket s = (Actual_Socket) sock;
     return s->private_ptr;
 }
 
@@ -678,7 +732,8 @@ void *sk_get_private_ptr(Socket s) {
 char *sk_addr_error(SockAddr addr) {
     return addr->error;
 }
-char *sk_socket_error(Socket s) {
+static char *sk_tcp_socket_error(Socket sock) {
+    Actual_Socket s = (Actual_Socket) sock;
     return s->error;
 }
 
@@ -686,10 +741,10 @@ char *sk_socket_error(Socket s) {
  * For Plink: enumerate all sockets currently active.
  */
 SOCKET first_socket(enum234 *e) {
-    Socket s = first234(sktree, e);
+    Actual_Socket s = first234(sktree, e);
     return s ? s->s : INVALID_SOCKET;
 }
 SOCKET next_socket(enum234 *e) {
-    Socket s = next234(e);
+    Actual_Socket s = next234(e);
     return s ? s->s : INVALID_SOCKET;
 }
index d0a1f99..8ace77d 100644 (file)
--- a/x11fwd.c
+++ b/x11fwd.c
@@ -62,12 +62,15 @@ extern void sshfwd_close(void *);
 extern void sshfwd_write(void *, char *, int);
 
 struct X11Private {
+    struct plug_function_table *fn;
+    /* the above variable absolutely *must* be the first in this structure */
     unsigned char firstpkt[12];        /* first X data packet */
     char *auth_protocol;
     unsigned char *auth_data;
     int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;
     int verified;
     void *c;                           /* data used by ssh.c */
+    Socket s;
 };
 
 void x11_close (Socket s);
@@ -103,23 +106,22 @@ static int x11_verify(char *proto, unsigned char *data, int dlen) {
     return 1;
 }
 
-static int x11_receive (Socket s, int urgent, char *data, int len) {
-    struct X11Private *pr = (struct X11Private *)sk_get_private_ptr(s);
+static int x11_closing (Plug plug, char *error_msg, int error_code, int calling_back) {
+    struct X11Private *pr = (struct X11Private *) plug;
+
+    /*
+     * We have no way to communicate down the forwarded connection,
+     * so if an error occurred on the socket, we just ignore it
+     * and treat it like a proper close.
+     */
+    sshfwd_close(pr->c);
+    x11_close(pr->s);
+    return 1;
+}
+
+static int x11_receive (Plug plug, int urgent, char *data, int len) {
+    struct X11Private *pr = (struct X11Private *) plug;
 
-    if (urgent==3) {
-        /*
-         * A socket error has occurred. We have no way to
-         * communicate this down the forwarded connection, so we'll
-         * just treat it like a proper close.
-         */
-        len = 0;
-    }
-    if (!len) {
-       /* Connection has closed. */
-        sshfwd_close(pr->c);
-       x11_close(s);
-        return 1;
-    }
     sshfwd_write(pr->c, data, len);
     return 1;
 }
@@ -131,6 +133,11 @@ static int x11_receive (Socket s, int urgent, char *data, int len) {
  * also, fills the SocketsStructure
  */
 char *x11_init (Socket *s, char *display, void *c) {
+    static struct plug_function_table fn_table = {
+       x11_closing,
+       x11_receive
+    };
+
     SockAddr addr;
     int port;
     char *err, *dummy_realhost;
@@ -163,16 +170,19 @@ char *x11_init (Socket *s, char *display, void *c) {
     /*
      * Open socket.
      */
-    *s = sk_new(addr, port, 0, 1, x11_receive);
-    if ( (err = sk_socket_error(*s)) )
-       return err;
-
     pr = (struct X11Private *)smalloc(sizeof(struct X11Private));
+    pr->fn = &fn_table;
     pr->auth_protocol = NULL;
     pr->verified = 0;
     pr->data_read = 0;
     pr->c = c;
 
+    pr->s = *s = sk_new(addr, port, 0, 1, (Plug) pr);
+    if ( (err = sk_socket_error(*s)) ) {
+       sfree (pr);
+       return err;
+    }
+
     sk_set_private_ptr(*s, pr);
     sk_addr_free(addr);
     return NULL;