*
* 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);
* 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
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;
}
* 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;
/*
* 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;
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;
* 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;
/*
* 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;
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) {
*/
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
/*
* 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;
}
}
-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;
* 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;
/*
* 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;
#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;
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. */
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;
}
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;
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;
SOCKADDR_IN a;
DWORD err;
char *errstr;
- Socket ret;
+ Actual_Socket ret;
extern char *do_select(SOCKET skt, int startup);
short localport;
* 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;
if (s == INVALID_SOCKET) {
err = WSAGetLastError();
ret->error = winsock_error_string(err);
- return ret;
+ return (Socket) ret;
}
ret->oobinline = oobinline;
if (err)
{
ret->error = winsock_error_string(err);
- return ret;
+ return (Socket) ret;
}
/*
{
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
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);
* 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;
}
}
-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.
*/
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.
*/
int ret, open;
DWORD err;
char buf[BUFFER_GRANULE];
- Socket s;
+ Actual_Socket s;
u_long atmark;
/* wParam is the socket itself */
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);
}
}
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:
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:
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;
}
* 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;
}
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;
}
* 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;
}
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);
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;
}
* 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;
/*
* 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;