diff --git a/src/syncterm/conn.c b/src/syncterm/conn.c
index d24bcb2da1803377fe34bbddd420991de65723df..d4fe974b5f8f1197df21f76254893db4783dbd61 100644
--- a/src/syncterm/conn.c
+++ b/src/syncterm/conn.c
@@ -5,207 +5,309 @@
 #include "gen_defs.h"
 #include "genwrap.h"
 #include "sockwrap.h"
+#include "threadwrap.h"
 
 #include "bbslist.h"
-#include "telnet_io.h"
-#include "conn.h"
 #include "uifcinit.h"
+#include "conn.h"
+#include "rlogin.h"
+#include "raw.h"
+#include "ssh.h"
+#include "conn_telnet.h"
 
-#include "st_crypt.h"
-
-static int	con_type=CONN_TYPE_UNKNOWN;
-SOCKET conn_socket=INVALID_SOCKET;
+struct conn_api conn_api;
 char *conn_types[]={"Unknown","RLogin","Telnet","Raw","SSH",NULL};
 int conn_ports[]={0,513,23,0,22};
-CRYPT_SESSION	ssh_session;
-int				ssh_active=FALSE;
 
-#if defined(__BORLANDC__)
-	#pragma argsused
-#endif
-BOOL conn_is_connected(void)
+struct conn_buffer conn_inbuf;
+struct conn_buffer conn_outbuf;
+
+/* Buffer functions */
+struct conn_buffer *create_conn_buf(struct conn_buffer *buf, size_t size)
 {
-	if(con_type==CONN_TYPE_SSH) {
-		if(!ssh_active)
-			return(FALSE);
+	buf->buf=(unsigned char *)malloc(size);
+	if(buf->buf==NULL)
+		return(NULL);
+	buf->bufsize=size;
+	buf->buftop=0;
+	buf->bufbot=0;
+	if(pthread_mutex_init(&(buf->mutex), NULL)) {
+		free(buf->buf);
+		return(NULL);
+	}
+	if(sem_init(&(buf->in_sem), 0, 0)) {
+		free(buf->buf);
+		pthread_mutex_destroy(&(buf->mutex));
+		return(NULL);
+	}
+	if(sem_init(&(buf->out_sem), 0, 0)) {
+		free(buf->buf);
+		pthread_mutex_destroy(&(buf->mutex));
+		sem_destroy(&(buf->in_sem));
+		return(NULL);
 	}
-	return socket_check(conn_socket,NULL,NULL,0);
 }
 
-int conn_recv(char *buffer, size_t buflen, unsigned timeout)
+void destroy_conn_buf(struct conn_buffer *buf)
 {
-	int		rd;
-	int		avail;
-	BYTE	*p;
-	BOOL	data_waiting;
-	static BYTE	*telnet_buf=NULL;
-	static size_t tbsize=0;
-
-	if(con_type==CONN_TYPE_SSH) {
-		int	status;
-		if(!socket_check(conn_socket, &data_waiting, NULL, timeout))
-			return(-1);
-
-		status=cl.PopData(ssh_session, buffer, buflen, &rd);
-		if(cryptStatusError(status)) {
-			char	str[1024];
-
-			if(status==CRYPT_ERROR_COMPLETE) {	/* connection closed */
-				ssh_active=FALSE;
-				return(-1);
-			}
-			sprintf(str,"Error %d recieving data",status);
-			uifcmsg("Error recieving data",str);
-			return(-1);
-		}
-		return(rd);
-	}
+	FREE_AND_NULL(buf->buf);
+	while(pthread_mutex_destroy(&(buf->mutex)));
+	while(sem_destroy(&(buf->in_sem)));
+	while(sem_destroy(&(buf->out_sem)));
+}
 
-	if(con_type == CONN_TYPE_TELNET) {
-		if(tbsize < buflen) {
-			p=(BYTE *)realloc(telnet_buf, buflen);
-			if(p != NULL) {
-				telnet_buf=p;
-				tbsize = buflen;
-			}
-		}
+/*
+ * The mutex should always be locked by the caller
+ * for the rest of the buffer functions
+ */
+size_t conn_buf_bytes(struct conn_buffer *buf)
+{
+	if(buf->buftop >= buf->bufbot)
+		return(buf->buftop-buf->bufbot);
+	return(buf->bufsize-buf->bufbot + buf->buftop);
+}
+
+size_t conn_buf_free(struct conn_buffer *buf)
+{
+	if(buf->buftop >= buf->bufbot)
+		return(buf->bufsize-buf->buftop-buf->bufbot);
+	return(buf->bufbot - buf->buftop);
+}
+
+/*
+ * Copies up to outlen bytes from the buffer into outbuf,
+ * leaving them in the buffer.  Returns the number of bytes
+ * copied out of the buffer
+ */
+size_t conn_buf_peek(struct conn_buffer *buf, unsigned char *outbuf, size_t outlen)
+{
+	size_t copy_bytes;
+	size_t chunk;
+
+	copy_bytes=conn_buf_bytes(buf);
+	if(copy_bytes > outlen)
+		copy_bytes=outlen;
+	chunk=buf->bufsize-buf->bufbot;
+	if(chunk > copy_bytes)
+		chunk=copy_bytes;
+
+	memcpy(outbuf, buf->buf+buf->bufbot, chunk);
+	if(chunk < copy_bytes)
+		memcpy(outbuf+chunk, buf->buf, copy_bytes-chunk);
+
+	return(copy_bytes);
+}
+
+/*
+ * Copies up to outlen bytes from the buffer into outbuf,
+ * removing them from the buffer.  Returns the number of
+ * bytes removed from the buffer.
+ */
+size_t conn_buf_get(struct conn_buffer *buf, unsigned char *outbuf, size_t outlen)
+{
+	size_t ret;
+	size_t loop;
+
+	ret=conn_buf_peek(buf, outbuf, outlen);
+	buf->bufbot+=ret;
+	if(buf->bufbot >= buf->bufsize)
+		buf->bufbot -= buf->bufsize;
+	sem_post(&(buf->out_sem));
+	return(ret);
+}
+
+/*
+ * Places up to outlen bytes from outbuf into the buffer
+ * returns the number of bytes written into the buffer
+ */
+size_t conn_buf_put(struct conn_buffer *buf, const unsigned char *outbuf, size_t outlen)
+{
+	size_t write_bytes;
+	size_t chunk;
+	size_t loop;
+
+	write_bytes=buf->bufsize-conn_buf_bytes(buf);
+	if(write_bytes > outlen)
+		write_bytes = outlen;
+	chunk=buf->bufsize-buf->buftop;
+	if(chunk > write_bytes)
+		chunk=write_bytes;
+	memcpy(buf->buf+buf->buftop, outbuf, chunk);
+	if(chunk < write_bytes)
+		memcpy(buf->buf, outbuf+chunk, write_bytes-chunk);
+	buf->buftop+=write_bytes;
+	if(buf->buftop >= buf->bufsize)
+		buf->buftop -= buf->bufsize;
+	sem_post(&(buf->in_sem));
+	return(write_bytes);
+}
+
+/*
+ * Waits up to timeout milliseconds for bcount bytes to be available/free
+ * in the buffer.
+ */
+size_t conn_buf_wait_cond(struct conn_buffer *buf, size_t bcount, unsigned long timeout, int free)
+{
+	long double now;
+	long double end;
+	size_t found;
+	size_t loop;
+	unsigned long timeleft;
+	int retnow=0;
+	sem_t	*sem;
+	size_t (*cond)(struct conn_buffer *buf);
+	
+	if(free) {
+		sem=&(buf->out_sem);
+		cond=conn_buf_free;
 	}
+	else {
+		sem=&(buf->in_sem);
+		cond=conn_buf_bytes;
+	}
+
+	found=cond(buf);
+	if(found > bcount)
+		found=bcount;
 
-	while(1) {
-
-		if(!socket_check(conn_socket, &data_waiting, NULL, timeout))
-			return(-1);
-
-		if(!data_waiting)
-			return(0);
-
-		if(!ioctlsocket(conn_socket,FIONREAD,(void *)&avail) && avail)
-			rd=recv(conn_socket,buffer,avail<(int)buflen?avail:buflen,0);
-		else
-			return(0);
-		if(con_type == CONN_TYPE_TELNET) {
-			if(rd>0) {
-				if(telnet_interpret(buffer, rd, telnet_buf, &rd)==telnet_buf) {
-					if(rd==0)	/* all bytes removed */
-						continue;
-					memcpy(buffer, telnet_buf, rd);
-				}
-			}
+	if(found == bcount || timeout==0)
+		return(found);
+
+	pthread_mutex_unlock(&(buf->mutex));
+
+	end=timeout;
+	end/=1000;
+	now=xp_timer();
+	end+=now;
+
+	for(;;) {
+		now=xp_timer();
+		if(end <= now)
+			timeleft=0;
+		else {
+			timeleft=(end-now)*1000;
+			if(timeleft<1 || timeleft > timeout)
+				timeleft=1;
 		}
-		break;
+		if(sem_trywait_block(sem, timeleft))
+			retnow=1;
+		pthread_mutex_lock(&(buf->mutex));
+		found=cond(buf);
+		if(found > bcount)
+			found=bcount;
+
+		if(found == bcount || retnow)
+			return(found);
+
+		pthread_mutex_unlock(&(buf->mutex));
 	}
-	return(rd);
+}
+
+/*
+ * Connection functions
+ */
+
+BOOL conn_connected(void)
+{
+	if(conn_api.input_thread_running && conn_api.output_thread_running)
+		return(TRUE);
+	return(FALSE);
+}
+
+int conn_recv(char *buffer, size_t buflen, unsigned timeout)
+{
+	size_t found;
+
+	pthread_mutex_lock(&(conn_inbuf.mutex));
+	found=conn_buf_wait_bytes(&conn_inbuf, buflen, timeout);
+	if(found)
+		found=conn_buf_get(&conn_inbuf, buffer, found);
+	pthread_mutex_unlock(&(conn_inbuf.mutex));
+	return(found);
+}
+
+int conn_peek(char *buffer, size_t buflen)
+{
+	size_t found;
+
+	pthread_mutex_lock(&(conn_inbuf.mutex));
+	found=conn_buf_wait_bytes(&conn_inbuf, buflen, 0);
+	if(found)
+		found=conn_buf_peek(&conn_inbuf, buffer, found);
+	pthread_mutex_unlock(&(conn_inbuf.mutex));
+	return(found);
 }
 
 int conn_send(char *buffer, size_t buflen, unsigned int timeout)
 {
-	int sent=0;
-	int	ret;
-	int	i;
-	BYTE *sendbuf;
-	BYTE *p;
-	static BYTE *outbuf=NULL;
-	static size_t obsize=0;
-
-	if(con_type==CONN_TYPE_SSH) {
-		int status;
-
-		sent=0;
-		while(sent<(int)buflen) {
-			status=cl.PushData(ssh_session, buffer+sent, buflen-sent, &i);
-			if(cryptStatusError(status)) {
-				char	str[1024];
-
-				if(status==CRYPT_ERROR_COMPLETE) {	/* connection closed */
-					ssh_active=FALSE;
-					return(-1);
-				}
-				sprintf(str,"Error %d sending data",status);
-				uifcmsg("Error sending data",str);
-				return(-1);
-			}
-			sent+=i;
-		}
-		cl.FlushData(ssh_session);
-		return(0);
-	}
+	size_t found;
+
+	pthread_mutex_lock(&(conn_outbuf.mutex));
+	found=conn_buf_wait_free(&conn_outbuf, buflen, timeout);
+	if(found)
+		found=conn_buf_put(&conn_outbuf, buffer, found);
+	pthread_mutex_unlock(&(conn_outbuf.mutex));
+	return(found);
+}
 
-	if(con_type == CONN_TYPE_TELNET) {
-		if(obsize < buflen*2) {
-			p=realloc(outbuf, buflen*2);
-			if(p!=NULL) {
-				outbuf=p;
-				obsize = 2 * buflen;
-			}
-			else
-				return(-1);
-		}
-		sendbuf=telnet_expand(buffer, buflen, outbuf, &buflen);
+int conn_connect(struct bbslist *bbs)
+{
+	memset(&conn_api, 0, sizeof(conn_api));
+
+	switch(bbs->conn_type) {
+		case CONN_TYPE_RLOGIN:
+			conn_api.connect=rlogin_connect;
+			conn_api.close=rlogin_close;
+			break;
+		case CONN_TYPE_TELNET:
+			conn_api.connect=telnet_connect;
+			conn_api.close=telnet_close;
+			conn_api.binary_mode_on=telnet_binary_mode_on;
+			conn_api.binary_mode_off=telnet_binary_mode_off;
+			break;
+		case CONN_TYPE_RAW:
+			conn_api.connect=raw_connect;
+			conn_api.close=raw_close;
+			break;
+		case CONN_TYPE_SSH:
+			conn_api.connect=ssh_connect;
+			conn_api.close=ssh_close;
+			break;
 	}
-	else
-		sendbuf = buffer;
-
-	while(sent<(int)buflen) {
-		if(!socket_check(conn_socket, NULL, &i, timeout))
-			return(-1);
-
-		if(!i)
-			return(-1);
-
-		ret=send(conn_socket,sendbuf+sent,buflen-sent,0);
-		if(ret==-1) {
-			switch(errno) {
-				case EAGAIN:
-				case ENOBUFS:
-					SLEEP(1);
-					break;
-				default:
-					return(-1);
-			}
-		}
-		else
-			sent+=ret;
+	if(conn_api.connect) {
+		conn_api.connect(bbs);
+		while(conn_api.terminate == 0 && (conn_api.input_thread_running == 0 || conn_api.output_thread_running == 0))
+			SLEEP(1);
 	}
-	return(0);
+	return(conn_api.terminate);
 }
 
-int conn_connect(struct bbslist *bbs)
+BOOL conn_data_waiting(void)
 {
+	size_t found;
+
+	pthread_mutex_lock(&(conn_inbuf.mutex));
+	found=conn_buf_bytes(&conn_inbuf);
+	pthread_mutex_unlock(&(conn_inbuf.mutex));
+	if(found)
+		return(TRUE);
+	return(FALSE);
+}
+
+int conn_close(void)
+{
+	if(conn_api.close)
+		return(conn_api.close());
+}
+
+int conn_socket_connect(struct bbslist *bbs)
+{
+	SOCKET	sock;
 	HOSTENT *ent;
 	SOCKADDR_IN	saddr;
 	char	*p;
 	unsigned int	neta;
-	char	*ruser;
-	char	*passwd;
-
-	init_uifc(TRUE, TRUE);
-
-	con_type=bbs->conn_type;
-	if(con_type==CONN_TYPE_SSH) {
-		if(!crypt_loaded)
-			init_crypt();
-		if(!crypt_loaded) {
-			uifcmsg("Cannot load cryptlib - SSH inoperative",	"`Cannot load cryptlib`\n\n"
-						"Cannot laod the file "
-#ifdef _WIN32
-						"cl32.dll"
-#else
-						"libcl.so"
-#endif
-						"\nThis file is required for SSH functionality.\n\n"
-						"The newest version is always available from:\n"
-						"http://www.cs.auckland.ac.nz/~pgut001/cryptlib/"
-						);
-			return(-1);
-		}
-	}
 
-	ruser=bbs->user;
-	passwd=bbs->password;
-	if(con_type==CONN_TYPE_RLOGIN && bbs->reversed) {
-		passwd=bbs->user;
-		ruser=bbs->password;
-	}
 	for(p=bbs->addr;*p;p++)
 		if(*p!='.' && !isdigit(*p))
 			break;
@@ -223,27 +325,29 @@ int conn_connect(struct bbslist *bbs)
 							"The system is unable to resolve the hostname... double check the spelling.\n"
 							"If it's not an issue with your DNS settings, the issue is probobly\n"
 							"with the DNS settings of the system you are trying to contact.");
-			return(-1);
+			conn_api.terminate=-1;
+			return(INVALID_SOCKET);
 		}
 		neta=*((unsigned int*)ent->h_addr_list[0]);
 		uifc.pop(NULL);
 	}
 	uifc.pop("Connecting...");
 
-	conn_socket=socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
-	if(conn_socket==INVALID_SOCKET) {
+	sock=socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
+	if(sock==INVALID_SOCKET) {
 		uifc.pop(NULL);
 		uifcmsg("Cannot create socket!",	"`Unable to create socket`\n\n"
 											"Your system is either dangerously low on resources, or there"
 											"is a problem with your TCP/IP stack.");
-		return(-1);
+		conn_api.terminate=-1;
+		return(INVALID_SOCKET);
 	}
 	memset(&saddr,0,sizeof(saddr));
 	saddr.sin_addr.s_addr = neta;
 	saddr.sin_family = AF_INET;
 	saddr.sin_port   = htons((WORD)bbs->port);
 
-	if(connect(conn_socket, (struct sockaddr *)&saddr, sizeof(saddr))) {
+	if(connect(sock, (struct sockaddr *)&saddr, sizeof(saddr))) {
 		char str[LIST_ADDR_MAX+20];
 
 		conn_close();
@@ -251,102 +355,21 @@ int conn_connect(struct bbslist *bbs)
 		sprintf(str,"Cannot connect to %s!",bbs->addr);
 		uifcmsg(str,	"`Unable to connect`\n\n"
 						"Cannot connect to the remote system... it is down or unreachable.");
-		return(-1);
+		conn_api.terminate=-1;
+		return(INVALID_SOCKET);
 	}
 
-	switch(con_type) {
-		case CONN_TYPE_TELNET:
-			memset(telnet_local_option,0,sizeof(telnet_local_option));
-			memset(telnet_remote_option,0,sizeof(telnet_remote_option));
-			break;
-
-		case CONN_TYPE_RLOGIN:
-			conn_send("",1,1000);
-			conn_send(passwd,strlen(passwd)+1,1000);
-			conn_send(ruser,strlen(ruser)+1,1000);
-			if(bbs->bpsrate) {
-				char	sbuf[30];
-				sprintf(sbuf, "ansi-bbs/%d", bbs->bpsrate);
-
-				conn_send(sbuf, strlen(sbuf)+1,1000);
-			}
-			else
-				conn_send("ansi-bbs/115200",15,1000);
-			break;
-		case CONN_TYPE_SSH: {
-			int off=1;
-			int status;
-			char password[MAX_PASSWD_LEN+1];
-
-			ssh_active=FALSE;
-
-			status=cl.CreateSession(&ssh_session, CRYPT_UNUSED, CRYPT_SESSION_SSH);
-			if(cryptStatusError(status)) {
-				char	str[1024];
-				sprintf(str,"Error %d creating session",status);
-				uifcmsg("Error creating session",str);
-				return(-1);
-			}
-
-			/* we need to disable Nagle on the socket. */
-			setsockopt(conn_socket, IPPROTO_TCP, TCP_NODELAY, ( char * )&off, sizeof ( off ) );
-
-			SAFECOPY(password,bbs->password);
-
-			/* Pass socket to cryptlib */
-			status=cl.SetAttribute(ssh_session, CRYPT_SESSINFO_NETWORKSOCKET, conn_socket);
-			if(cryptStatusError(status)) {
-				char	str[1024];
-				sprintf(str,"Error %d passing socket",status);
-				uifcmsg("Error passing socket",str);
-				return(-1);
-			}
-
-			/* Add username/password */
-			status=cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_USERNAME, ruser, strlen(ruser));
-			if(cryptStatusError(status)) {
-				char	str[1024];
-				sprintf(str,"Error %d setting username",status);
-				uifcmsg("Error setting username",str);
-				return(-1);
-			}
-
-			if(!password[0])
-				uifcinput("Password",MAX_PASSWD_LEN,password,K_PASSWORD,"Incorrect password.  Try again.");
-
-			status=cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_PASSWORD, passwd, strlen(passwd));
-			if(cryptStatusError(status)) {
-				char	str[1024];
-				sprintf(str,"Error %d setting password",status);
-				uifcmsg("Error setting password",str);
-				return(-1);
-			}
-
-			/* Activate the session */
-			status=cl.SetAttribute(ssh_session, CRYPT_SESSINFO_ACTIVE, 1);
-			if(cryptStatusError(status)) {
-				char	str[1024];
-
-				sprintf(str,"Error %d activating session",status);
-				uifcmsg("Error activating session",str);
-				return(-1);
-			}
-
-			ssh_active=TRUE;
-			break;
-		}
-	}
-	uifc.pop(NULL);
+	return(sock);
+}
 
-	return(0);
+void conn_binary_mode_on(void)
+{
+	if(conn_api.binary_mode_on)
+		conn_api.binary_mode_on();
 }
 
-int conn_close(void)
+void conn_binary_mode_off(void)
 {
-	if(con_type==CONN_TYPE_SSH) {
-		cl.DestroySession(ssh_session);
-		ssh_active=FALSE;
-		return(0);
-	}
-	return(closesocket(conn_socket));
+	if(conn_api.binary_mode_off)
+		conn_api.binary_mode_off();
 }
diff --git a/src/syncterm/conn.h b/src/syncterm/conn.h
index de07b7d72693af68cb510e8e9c3111fd9bc5edc9..774ef9a1bbc496ca40e06fccae35fe5382a5229f 100644
--- a/src/syncterm/conn.h
+++ b/src/syncterm/conn.h
@@ -4,10 +4,10 @@
 #define _CONN_H_
 
 #include "sockwrap.h"
+#include "threadwrap.h"
 
 #include "bbslist.h"
 
-extern SOCKET conn_socket;
 extern char *conn_types[];
 extern int conn_ports[];
 
@@ -20,11 +20,64 @@ enum {
 	,CONN_TYPE_TERMINATOR
 };
 
+struct conn_api {
+	int (*connect)(struct bbslist *bbs);
+	int (*close)(void);
+	void (*binary_mode_on)(void);
+	void (*binary_mode_off)(void);
+	int log_level;
+	int type;
+	int input_thread_running;
+	int output_thread_running;
+	int terminate;
+	unsigned char *rd_buf;
+	size_t rd_buf_size;
+	unsigned char *wr_buf;
+	size_t wr_buf_size;
+};
+
+struct conn_buffer {
+	unsigned char	*buf;
+	size_t			bufsize;
+	size_t			buftop;
+	size_t			bufbot;
+	pthread_mutex_t	mutex;
+	sem_t			in_sem;
+	sem_t			out_sem;
+};
+
+/*
+ * Functions for stuff using connections
+ */
 int conn_recv(char *buffer, size_t buflen, unsigned int timeout);
+int conn_peek(char *buffer, size_t buflen);
 int conn_send(char *buffer, size_t buflen, unsigned int timeout);
 int conn_connect(struct bbslist *bbs);
 int conn_close(void);
-BOOL conn_is_connected(void);
-void conn_settype(int type);
+BOOL conn_connected(void);
+BOOL conn_data_waiting(void);
+void conn_binary_mode_on(void);
+void conn_binary_mode_off(void);
+
+/*
+ * For connection providers
+ */
+
+#define BUFFER_SIZE	16384
+
+extern struct conn_buffer conn_inbuf;
+extern struct conn_buffer conn_outbuf;
+extern struct conn_api conn_api;
+
+struct conn_buffer *create_conn_buf(struct conn_buffer *buf, size_t size);
+void destroy_conn_buf(struct conn_buffer *buf);
+size_t conn_buf_bytes(struct conn_buffer *buf);
+size_t conn_buf_peek(struct conn_buffer *buf, unsigned char *outbuf, size_t outlen);
+size_t conn_buf_get(struct conn_buffer *buf, unsigned char *outbuf, size_t outlen);
+size_t conn_buf_put(struct conn_buffer *buf, const unsigned char *outbuf, size_t outlen);
+size_t conn_buf_wait_cond(struct conn_buffer *buf, size_t bcount, unsigned long timeout, int free);
+#define conn_buf_wait_bytes(buf, count, timeout)	conn_buf_wait_cond(buf, count, timeout, 0)
+#define conn_buf_wait_free(buf, count, timeout)	conn_buf_wait_cond(buf, count, timeout, 1)
+int conn_socket_connect(struct bbslist *bbs);
 
 #endif
diff --git a/src/syncterm/conn_telnet.c b/src/syncterm/conn_telnet.c
new file mode 100644
index 0000000000000000000000000000000000000000..611a96fd287ee524eda892053b6699c2e3334e85
--- /dev/null
+++ b/src/syncterm/conn_telnet.c
@@ -0,0 +1,148 @@
+/* $Id$ */
+
+#include <stdlib.h>
+
+#include "gen_defs.h"
+#include "genwrap.h"
+#include "sockwrap.h"
+#include "threadwrap.h"
+
+#include "bbslist.h"
+#include "conn.h"
+#include "uifcinit.h"
+
+#include "telnet_io.h"
+
+SOCKET telnet_sock=INVALID_SOCKET;
+
+void telnet_input_thread(void *args)
+{
+	fd_set	rds;
+	int		rd;
+	size_t	buffered;
+	size_t	buffer;
+	char	rbuf[BUFFER_SIZE];
+	char	*buf;
+
+	conn_api.input_thread_running=1;
+	while(telnet_sock != INVALID_SOCKET && !conn_api.terminate) {
+		FD_ZERO(&rds);
+		FD_SET(telnet_sock, &rds);
+		rd=select(telnet_sock+1, &rds, NULL, NULL, NULL);
+		if(rd==-1) {
+			if(errno==EBADF)
+				break;
+			rd=0;
+		}
+		if(rd==1) {
+			rd=recv(telnet_sock, conn_api.rd_buf, conn_api.rd_buf_size, MSG_DONTWAIT);
+			if(rd <= 0)
+				break;
+		}
+		if(rd>0)
+			buf=telnet_interpret(conn_api.rd_buf, rd, rbuf, &rd);
+		buffered=0;
+		while(buffered < rd) {
+			pthread_mutex_lock(&(conn_inbuf.mutex));
+			buffer=conn_buf_wait_free(&conn_inbuf, rd-buffered, 100);
+			buffered+=conn_buf_put(&conn_inbuf, buf+buffered, buffer);
+			pthread_mutex_unlock(&(conn_inbuf.mutex));
+		}
+	}
+	conn_api.input_thread_running=0;
+}
+
+void telnet_output_thread(void *args)
+{
+	fd_set	wds;
+	int		wr;
+	int		ret;
+	size_t	sent;
+	size_t	send;
+	char	ebuf[BUFFER_SIZE*2];
+	char	*buf;
+
+	conn_api.output_thread_running=1;
+	while(telnet_sock != INVALID_SOCKET && !conn_api.terminate) {
+		pthread_mutex_lock(&(conn_outbuf.mutex));
+		wr=conn_buf_wait_bytes(&conn_outbuf, 1, 100);
+		if(wr) {
+			wr=conn_buf_get(&conn_outbuf, conn_api.wr_buf, conn_api.wr_buf_size);
+			pthread_mutex_unlock(&(conn_outbuf.mutex));
+			buf=telnet_expand(conn_api.wr_buf, wr, ebuf, &wr);
+			sent=0;
+			while(sent < wr) {
+				FD_ZERO(&wds);
+				FD_SET(telnet_sock, &wds);
+				ret=select(telnet_sock+1, NULL, &wds, NULL, NULL);
+				if(ret==-1) {
+					if(errno==EBADF)
+						break;
+					ret=0;
+				}
+				if(ret==1) {
+					ret=sendsocket(telnet_sock, buf+sent, wr-sent);
+					if(ret==-1)
+						break;
+					sent+=ret;
+				}
+			}
+		}
+		else
+			pthread_mutex_unlock(&(conn_outbuf.mutex));
+		if(ret==-1)
+			break;
+	}
+	conn_api.output_thread_running=0;
+}
+
+int telnet_connect(struct bbslist *bbs)
+{
+	init_uifc(TRUE, TRUE);
+
+	telnet_sock=conn_socket_connect(bbs);
+	if(telnet_sock==INVALID_SOCKET)
+		return(-1);
+
+	create_conn_buf(&conn_inbuf, BUFFER_SIZE);
+	create_conn_buf(&conn_outbuf, BUFFER_SIZE);
+	conn_api.rd_buf=(unsigned char *)malloc(BUFFER_SIZE);
+	conn_api.rd_buf_size=BUFFER_SIZE;
+	conn_api.wr_buf=(unsigned char *)malloc(BUFFER_SIZE);
+	conn_api.wr_buf_size=BUFFER_SIZE;
+
+	memset(telnet_local_option,0,sizeof(telnet_local_option));
+	memset(telnet_remote_option,0,sizeof(telnet_remote_option));
+
+	_beginthread(telnet_output_thread, 0, NULL);
+	_beginthread(telnet_input_thread, 0, NULL);
+
+	uifc.pop(NULL);
+
+	return(0);
+}
+
+int telnet_close(void)
+{
+	conn_api.terminate=1;
+	closesocket(telnet_sock);
+	while(conn_api.input_thread_running || conn_api.output_thread_running)
+		SLEEP(1);
+	destroy_conn_buf(&conn_inbuf);
+	destroy_conn_buf(&conn_outbuf);
+	FREE_AND_NULL(conn_api.rd_buf);
+	FREE_AND_NULL(conn_api.wr_buf);
+	return(0);
+}
+
+void telnet_binary_mode_on(void)
+{
+	request_telnet_opt(TELNET_DO,TELNET_BINARY_TX);
+	request_telnet_opt(TELNET_WILL,TELNET_BINARY_TX);
+}
+
+void telnet_binary_mode_off(void)
+{
+	request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
+	request_telnet_opt(TELNET_WONT,TELNET_BINARY_TX);
+}
diff --git a/src/syncterm/conn_telnet.h b/src/syncterm/conn_telnet.h
new file mode 100644
index 0000000000000000000000000000000000000000..21c06fd2b5e04966d9c3e55e79ecc53fe0f4a11d
--- /dev/null
+++ b/src/syncterm/conn_telnet.h
@@ -0,0 +1,12 @@
+#ifndef _TELNET_H_
+#define _TELNET_H_
+
+#include "sockwrap.h"
+
+extern SOCKET telnet_sock;
+void telnet_binary_mode_on(void);
+void telnet_binary_mode_off(void);
+int telnet_connect(struct bbslist *bbs);
+int telnet_close(void);
+
+#endif
diff --git a/src/syncterm/objects.mk b/src/syncterm/objects.mk
index 4524e154dbe840e1ed45de7c8a329e5e2823e3ab..42b01623ba333f364a1cdbfce8dbd8a31d47fcd9 100644
--- a/src/syncterm/objects.mk
+++ b/src/syncterm/objects.mk
@@ -3,9 +3,12 @@ OBJS = \
                         $(MTOBJODIR)$(DIRSEP)uifcinit$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)filepick$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)fonts$(OFILE) \
+                        $(MTOBJODIR)$(DIRSEP)rlogin$(OFILE) \
+                        $(MTOBJODIR)$(DIRSEP)ssh$(OFILE) \
+                        $(MTOBJODIR)$(DIRSEP)telnet_io$(OFILE) \
+                        $(MTOBJODIR)$(DIRSEP)conn_telnet$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)conn$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)telnet$(OFILE) \
-                        $(MTOBJODIR)$(DIRSEP)telnet_io$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)term$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)window$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)menu$(OFILE) \
@@ -14,3 +17,4 @@ OBJS = \
                         $(MTOBJODIR)$(DIRSEP)zmodem$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)st_crypt$(OFILE) \
                         $(MTOBJODIR)$(DIRSEP)syncterm$(OFILE)
+
diff --git a/src/syncterm/raw.h b/src/syncterm/raw.h
new file mode 100644
index 0000000000000000000000000000000000000000..6660b48d1117f5b092f52e003c4a58283e954541
--- /dev/null
+++ b/src/syncterm/raw.h
@@ -0,0 +1,9 @@
+#ifndef _RAW_H_
+#define _RAW_H_
+
+#include "rlogin.h"
+
+#define raw_connect rlogin_connect
+#define raw_close rlogin_close
+
+#endif
diff --git a/src/syncterm/rlogin.c b/src/syncterm/rlogin.c
new file mode 100644
index 0000000000000000000000000000000000000000..85f6487e326ac88e9457f62829a2d3eadacfbd6c
--- /dev/null
+++ b/src/syncterm/rlogin.c
@@ -0,0 +1,145 @@
+/* $Id$ */
+
+#include <stdlib.h>
+
+#include "sockwrap.h"
+
+#include "bbslist.h"
+#include "conn.h"
+#include "uifcinit.h"
+
+static SOCKET sock=INVALID_SOCKET;
+
+void rlogin_input_thread(void *args)
+{
+	fd_set	rds;
+	int		rd;
+	size_t	buffered;
+	size_t	buffer;
+
+	conn_api.input_thread_running=1;
+	while(sock != INVALID_SOCKET && !conn_api.terminate) {
+		FD_ZERO(&rds);
+		FD_SET(sock, &rds);
+		rd=select(sock+1, &rds, NULL, NULL, NULL);
+		if(rd==-1) {
+			if(errno==EBADF)
+				break;
+			rd=0;
+		}
+		if(rd==1) {
+			rd=recv(sock, conn_api.rd_buf, conn_api.rd_buf_size, MSG_DONTWAIT);
+			if(rd <= 0)
+				break;
+		}
+		buffered=0;
+		while(buffered < rd) {
+			pthread_mutex_lock(&(conn_inbuf.mutex));
+			buffer=conn_buf_wait_free(&conn_inbuf, rd-buffered, 100);
+			buffered+=conn_buf_put(&conn_inbuf, conn_api.rd_buf+buffered, buffer);
+			pthread_mutex_unlock(&(conn_inbuf.mutex));
+		}
+	}
+	conn_api.input_thread_running=0;
+}
+
+void rlogin_output_thread(void *args)
+{
+	fd_set	wds;
+	int		wr;
+	int		ret;
+	size_t	sent;
+	size_t	send;
+
+	conn_api.output_thread_running=1;
+	while(sock != INVALID_SOCKET && !conn_api.terminate) {
+		pthread_mutex_lock(&(conn_outbuf.mutex));
+		wr=conn_buf_wait_bytes(&conn_outbuf, 1, 100);
+		if(wr) {
+			wr=conn_buf_get(&conn_outbuf, conn_api.wr_buf, conn_api.wr_buf_size);
+			pthread_mutex_unlock(&(conn_outbuf.mutex));
+			sent=0;
+			while(sent < wr) {
+				FD_ZERO(&wds);
+				FD_SET(sock, &wds);
+				ret=select(sock+1, NULL, &wds, NULL, NULL);
+				if(ret==-1) {
+					if(errno==EBADF)
+						break;
+					ret=0;
+				}
+				if(ret==1) {
+					ret=sendsocket(sock, conn_api.wr_buf+sent, wr-sent);
+					if(ret==-1)
+						break;
+					sent+=ret;
+				}
+			}
+		}
+		else
+			pthread_mutex_unlock(&(conn_outbuf.mutex));
+		if(ret==-1)
+			break;
+	}
+	conn_api.output_thread_running=0;
+}
+
+int rlogin_connect(struct bbslist *bbs)
+{
+	char	*ruser;
+	char	*passwd;
+
+	init_uifc(TRUE, TRUE);
+
+	ruser=bbs->user;
+	passwd=bbs->password;
+	if(bbs->conn_type==CONN_TYPE_RLOGIN && bbs->reversed) {
+		passwd=bbs->user;
+		ruser=bbs->password;
+	}
+
+	sock=conn_socket_connect(bbs);
+	if(sock==INVALID_SOCKET)
+		return(-1);
+
+	create_conn_buf(&conn_inbuf, BUFFER_SIZE);
+	create_conn_buf(&conn_outbuf, BUFFER_SIZE);
+	conn_api.rd_buf=(unsigned char *)malloc(BUFFER_SIZE);
+	conn_api.rd_buf_size=BUFFER_SIZE;
+	conn_api.wr_buf=(unsigned char *)malloc(BUFFER_SIZE);
+	conn_api.wr_buf_size=BUFFER_SIZE;
+
+	if(bbs->conn_type == CONN_TYPE_RLOGIN) {
+		conn_send("",1,1000);
+		conn_send(passwd,strlen(passwd)+1,1000);
+		conn_send(ruser,strlen(ruser)+1,1000);
+		if(bbs->bpsrate) {
+			char	sbuf[30];
+			sprintf(sbuf, "ansi-bbs/%d", bbs->bpsrate);
+
+			conn_send(sbuf, strlen(sbuf)+1,1000);
+		}
+		else
+			conn_send("ansi-bbs/115200",15,1000);
+	}
+
+	_beginthread(rlogin_output_thread, 0, NULL);
+	_beginthread(rlogin_input_thread, 0, NULL);
+
+	uifc.pop(NULL);
+
+	return(0);
+}
+
+int rlogin_close(void)
+{
+	conn_api.terminate=1;
+	closesocket(sock);
+	while(conn_api.input_thread_running || conn_api.output_thread_running)
+		SLEEP(1);
+	destroy_conn_buf(&conn_inbuf);
+	destroy_conn_buf(&conn_outbuf);
+	FREE_AND_NULL(conn_api.rd_buf);
+	FREE_AND_NULL(conn_api.wr_buf);
+	return(0);
+}
diff --git a/src/syncterm/rlogin.h b/src/syncterm/rlogin.h
new file mode 100644
index 0000000000000000000000000000000000000000..c62d164156a7610ad7fc8ee3c97c2ebedbe3b7d4
--- /dev/null
+++ b/src/syncterm/rlogin.h
@@ -0,0 +1,7 @@
+#ifndef _RLOGIN_H_
+#define _RLOGIN_H_
+
+int rlogin_connect(struct bbslist *bbs);
+int rlogin_close(void);
+
+#endif
diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c
new file mode 100644
index 0000000000000000000000000000000000000000..f46eaf26580dd5abb7ed1c606ea742f8472d4e8f
--- /dev/null
+++ b/src/syncterm/ssh.c
@@ -0,0 +1,234 @@
+/* $Id$ */
+
+#include <stdlib.h>
+
+#include "gen_defs.h"
+#include "genwrap.h"
+#include "sockwrap.h"
+#include "threadwrap.h"
+
+#include "bbslist.h"
+#include "conn.h"
+#include "uifcinit.h"
+
+#include "st_crypt.h"
+
+static SOCKET	sock;
+CRYPT_SESSION	ssh_session;
+int				ssh_active=FALSE;
+
+void ssh_input_thread(void *args)
+{
+	fd_set	rds;
+	int status;
+	int rd;
+	size_t	buffered;
+	size_t	buffer;
+
+	conn_api.input_thread_running=1;
+	while(ssh_active && !conn_api.terminate) {
+		FD_ZERO(&rds);
+		FD_SET(sock, &rds);
+		rd=select(sock+1, &rds, NULL, NULL, NULL);
+		if(rd==-1) {
+			if(errno==EBADF)
+				break;
+			rd=0;
+		}
+		while(rd) {
+			status=cl.PopData(ssh_session, conn_api.rd_buf, conn_api.rd_buf_size, &rd);
+			if(cryptStatusError(status)) {
+				char	str[2048];
+				int		err_len;
+
+				if(status==CRYPT_ERROR_COMPLETE) {	/* connection closed */
+					ssh_active=FALSE;
+					break;
+				}
+				sprintf(str,"Error %d recieving data",status);
+				strcat(str,"\r\n\r\n");
+				err_len=sizeof(str)-strlen(str)-1;
+				cl.GetAttributeString(ssh_session, CRYPT_ATTRIBUTE_INT_ERRORMESSAGE, str+strlen(str), &err_len);
+				uifcmsg("Error recieving data",str);
+			}
+			else {
+				buffered=0;
+				while(buffered < rd) {
+					pthread_mutex_lock(&(conn_inbuf.mutex));
+					buffer=conn_buf_wait_free(&conn_inbuf, rd-buffered, 100);
+					buffered+=conn_buf_put(&conn_inbuf, conn_api.rd_buf+buffered, buffer);
+					pthread_mutex_unlock(&(conn_inbuf.mutex));
+				}
+			}
+		}
+	}
+	conn_api.input_thread_running=0;
+}
+
+void ssh_output_thread(void *args)
+{
+	fd_set	wds;
+	int		wr;
+	int		ret;
+	size_t	sent;
+	size_t	send;
+	int		status;
+
+	conn_api.output_thread_running=1;
+	while(ssh_active && !conn_api.terminate) {
+		pthread_mutex_lock(&(conn_outbuf.mutex));
+		wr=conn_buf_wait_bytes(&conn_outbuf, 1, 100);
+		pthread_mutex_unlock(&(conn_outbuf.mutex));
+		if(wr) {
+			pthread_mutex_lock(&(conn_outbuf.mutex));
+			wr=conn_buf_get(&conn_outbuf, conn_api.wr_buf, conn_api.wr_buf_size);
+			pthread_mutex_unlock(&(conn_outbuf.mutex));
+			sent=0;
+			while(sent < wr) {
+				status=cl.PushData(ssh_session, conn_api.wr_buf+sent, wr-sent, &ret);
+				if(cryptStatusError(status)) {
+					char	str[2048];
+					int		err_len;
+
+					if(status==CRYPT_ERROR_COMPLETE) {	/* connection closed */
+						ssh_active=FALSE;
+						break;
+					}
+					sprintf(str,"Error %d sending data",status);
+					strcat(str,"\r\n\r\n");
+					err_len=sizeof(str)-strlen(str)-1;
+					cl.GetAttributeString(ssh_session, CRYPT_ATTRIBUTE_INT_ERRORMESSAGE, str+strlen(str), &err_len);
+					uifcmsg("Error sending data",str);
+				}
+				sent += ret;
+			}
+			if(sent)
+				cl.FlushData(ssh_session);
+		}
+	}
+	conn_api.output_thread_running=0;
+}
+
+int ssh_connect(struct bbslist *bbs)
+{
+	int off=1;
+	int status;
+	char password[MAX_PASSWD_LEN+1];
+
+	init_uifc(TRUE, TRUE);
+
+	if(!crypt_loaded)
+		init_crypt();
+	if(!crypt_loaded) {
+		uifcmsg("Cannot load cryptlib - SSH inoperative",	"`Cannot load cryptlib`\n\n"
+					"Cannot laod the file "
+#ifdef _WIN32
+					"cl32.dll"
+#else
+					"libcl.so"
+#endif
+					"\nThis file is required for SSH functionality.\n\n"
+					"The newest version is always available from:\n"
+					"http://www.cs.auckland.ac.nz/~pgut001/cryptlib/"
+					);
+		return(conn_api.terminate=-1);
+	}
+
+	sock=conn_socket_connect(bbs);
+	if(sock==INVALID_SOCKET)
+		return(-1);
+
+	ssh_active=FALSE;
+
+	status=cl.CreateSession(&ssh_session, CRYPT_UNUSED, CRYPT_SESSION_SSH);
+	if(cryptStatusError(status)) {
+		char	str[1024];
+		sprintf(str,"Error %d creating session",status);
+		uifcmsg("Error creating session",str);
+		conn_api.terminate=1;
+		return(-1);
+	}
+
+	/* we need to disable Nagle on the socket. */
+	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, ( char * )&off, sizeof ( off ) );
+
+	SAFECOPY(password,bbs->password);
+
+	/* Add username/password */
+	status=cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_USERNAME, bbs->user, strlen(bbs->user));
+	if(cryptStatusError(status)) {
+		char	str[1024];
+		sprintf(str,"Error %d setting username",status);
+		uifcmsg("Error setting username",str);
+		conn_api.terminate=1;
+		return(-1);
+	}
+
+	if(!password[0])
+		uifcinput("Password",MAX_PASSWD_LEN,password,K_PASSWORD,"Incorrect password.  Try again.");
+
+	status=cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_PASSWORD, password, strlen(password));
+	if(cryptStatusError(status)) {
+		char	str[1024];
+		sprintf(str,"Error %d setting password",status);
+		uifcmsg("Error setting password",str);
+		conn_api.terminate=1;
+		return(-1);
+	}
+
+	/* Pass socket to cryptlib */
+	status=cl.SetAttribute(ssh_session, CRYPT_SESSINFO_NETWORKSOCKET, sock);
+	if(cryptStatusError(status)) {
+		char	str[1024];
+		sprintf(str,"Error %d passing socket",status);
+		uifcmsg("Error passing socket",str);
+		conn_api.terminate=1;
+		return(-1);
+	}
+
+	/* Activate the session */
+	status=cl.SetAttribute(ssh_session, CRYPT_SESSINFO_ACTIVE, 1);
+	if(cryptStatusError(status)) {
+		char	str[2048];
+		int err_len;
+
+		sprintf(str,"Error %d activating session",status);
+		strcat(str,"\r\n\r\n");
+		err_len=sizeof(str)-strlen(str)-1;
+		cl.GetAttributeString(ssh_session, CRYPT_ATTRIBUTE_INT_ERRORMESSAGE, str+strlen(str), &err_len);
+		uifcmsg("Error activating session",str);
+		conn_api.terminate=1;
+		return(-1);
+	}
+
+	ssh_active=TRUE;
+
+	create_conn_buf(&conn_inbuf, BUFFER_SIZE);
+	create_conn_buf(&conn_outbuf, BUFFER_SIZE);
+	conn_api.rd_buf=(unsigned char *)malloc(BUFFER_SIZE);
+	conn_api.rd_buf_size=BUFFER_SIZE;
+	conn_api.wr_buf=(unsigned char *)malloc(BUFFER_SIZE);
+	conn_api.wr_buf_size=BUFFER_SIZE;
+
+	_beginthread(ssh_output_thread, 0, NULL);
+	_beginthread(ssh_input_thread, 0, NULL);
+
+	uifc.pop(NULL);
+
+	return(0);
+}
+
+int ssh_close(void)
+{
+	conn_api.terminate=1;
+	cl.DestroySession(ssh_session);
+	ssh_active=FALSE;
+	while(conn_api.input_thread_running || conn_api.output_thread_running)
+		SLEEP(1);
+	closesocket(sock);
+	destroy_conn_buf(&conn_inbuf);
+	destroy_conn_buf(&conn_outbuf);
+	FREE_AND_NULL(conn_api.rd_buf);
+	FREE_AND_NULL(conn_api.wr_buf);
+	return(0);
+}
diff --git a/src/syncterm/ssh.h b/src/syncterm/ssh.h
new file mode 100644
index 0000000000000000000000000000000000000000..37d94f8ffc75736610907ef89eb93f79bbdd5ab3
--- /dev/null
+++ b/src/syncterm/ssh.h
@@ -0,0 +1,7 @@
+#ifndef _SSH_H_
+#define _SSH_H_
+
+int ssh_connect(struct bbslist *bbs);
+int ssh_close(void);
+
+#endif
diff --git a/src/syncterm/st_crypt.c b/src/syncterm/st_crypt.c
index ace3177161e682416c3278fe2028deb39a84f4cd..78d59e857f507ccb97a9ccf3d314ad7b12de4641 100644
--- a/src/syncterm/st_crypt.c
+++ b/src/syncterm/st_crypt.c
@@ -18,6 +18,7 @@ int init_crypt(void)
 	cl.End=cryptEnd;
 	cl.CreateSession=cryptCreateSession;
 	cl.GetAttribute=cryptGetAttribute;
+	cl.GetAttributeString=cryptGetAttributeString;
 	cl.SetAttribute=cryptSetAttribute;
 	cl.SetAttributeString=cryptSetAttributeString;
 	cl.DestroySession=cryptDestroySession;
@@ -61,6 +62,10 @@ int init_crypt(void)
 		FreeLibrary(cryptlib);
 		return(-1);
 	}
+	if((cl.GetAttributeString=GetProcAddress(cryptlib,"cryptGetAttributeString"))==NULL) {
+		FreeLibrary(cryptlib);
+		return(-1);
+	}
 	if((cl.SetAttribute=GetProcAddress(cryptlib,"cryptSetAttribute"))==NULL) {
 		FreeLibrary(cryptlib);
 		return(-1);
@@ -114,6 +119,10 @@ int init_crypt(void)
 		dlclose(cryptlib);
 		return(-1);
 	}
+	if((cl.GetAttributeString=dlsym(cryptlib,"cryptGetAttributeString"))==NULL) {
+		dlclose(cryptlib);
+		return(-1);
+	}
 	if((cl.SetAttribute=dlsym(cryptlib,"cryptSetAttribute"))==NULL) {
 		dlclose(cryptlib);
 		return(-1);
diff --git a/src/syncterm/st_crypt.h b/src/syncterm/st_crypt.h
index 40d3c19f098b30c010b67acb1a40bbd1f4fbd70b..e1a6afdd4e2cab247244fc4deca400b9c09281d6 100644
--- a/src/syncterm/st_crypt.h
+++ b/src/syncterm/st_crypt.h
@@ -17,6 +17,10 @@ struct crypt_funcs {
 	C_RET (*GetAttribute)( C_IN CRYPT_HANDLE cryptHandle,
 		C_IN CRYPT_ATTRIBUTE_TYPE attributeType,
 		C_OUT int C_PTR value );
+	C_RET (*GetAttributeString)( C_IN CRYPT_HANDLE cryptHandle,
+		C_IN CRYPT_ATTRIBUTE_TYPE attributeType,
+		C_OUT void C_PTR value,
+		C_OUT int C_PTR valueLength );
 	C_RET (*SetAttribute)( C_IN CRYPT_HANDLE cryptHandle,
 		C_IN CRYPT_ATTRIBUTE_TYPE attributeType,
 		C_IN int value );
diff --git a/src/syncterm/telnet_io.c b/src/syncterm/telnet_io.c
index d69d72d81c2ab2ebc530c086ed2621c9fd53b684..3bf58ea9acff09ee9c98a18e22fa64b80d356cdc 100644
--- a/src/syncterm/telnet_io.c
+++ b/src/syncterm/telnet_io.c
@@ -12,6 +12,7 @@
 #include "bbslist.h"
 #include "conn.h"
 #include "uifcinit.h"
+#include "conn_telnet.h"
 
 #define TELNET_TERM_MAXLEN	40
 
@@ -49,7 +50,7 @@ void putcom(BYTE* buf, size_t len)
 		p+=sprintf(p,"%02X ", buf[i]);
 
 	lprintf(LOG_DEBUG,"TX: %s", str);
-	send(conn_socket, buf, len, 0);
+	send(telnet_sock, buf, len, 0);
 }
 
 static void send_telnet_cmd(uchar cmd, uchar opt)
diff --git a/src/syncterm/term.c b/src/syncterm/term.c
index 3c67adb01814ac437f40f32be7c058839a3df410..d78b7c2b67ada92d87997475aeac4bd5ccaf4cd8 100644
--- a/src/syncterm/term.c
+++ b/src/syncterm/term.c
@@ -23,12 +23,7 @@
 #include "gutsz.h"
 #endif
 
-#ifdef GUTS_BUILTIN
-#define	BUFSIZE	1
-#else
-#define	BUFSIZE	2048
-#endif
-static char recvbuf[BUFSIZE];
+#define	ANSI_REPLY_BUFSIZE	2048
 
 #define DUMP
 
@@ -200,7 +195,6 @@ int log_level = LOG_INFO;
 
 enum { ZMODEM_MODE_SEND, ZMODEM_MODE_RECV } zmodem_mode;
 
-
 static BOOL zmodem_check_abort(void* vp)
 {
 	zmodem_t* zm = (zmodem_t*)vp;
@@ -219,7 +213,6 @@ static BOOL zmodem_check_abort(void* vp)
 
 extern FILE* log_fp;
 extern char *log_levels[];
-extern int	telnet_log_level;
 
 #if defined(__BORLANDC__)
 	#pragma argsused
@@ -365,56 +358,21 @@ void zmodem_progress(void* cbdata, ulong current_pos)
 #if defined(__BORLANDC__)
 	#pragma argsused
 #endif
-static int send_byte(void* unused, uchar ch, unsigned timeout)
+static int send_byte(void* unused, uchar ch, unsigned timeout /* seconds */)
 {
-	return conn_send(&ch,sizeof(char),timeout*1000);
+	return conn_send(&ch,sizeof(ch),timeout*1000);
 }
 
-static	ulong	bufbot;
-static	ulong	buftop;
-
 #if defined(__BORLANDC__)
 	#pragma argsused
 #endif
-static int recv_byte(void* unused, unsigned timeout)
+static int recv_byte(void* unused, unsigned timeout /* seconds */)
 {
 	BYTE	ch;
-	int		i;
-	time_t start=time(NULL);
-
-	if(bufbot==buftop) {
-		if((i=conn_recv(recvbuf,sizeof(recvbuf),timeout*1000))<1) {
-			if(timeout)
-				lprintf(LOG_ERR,"RECEIVE ERROR %d (after %u seconds, timeout=%u)"
-					,i, time(NULL)-start, timeout);
-			return(-1);
-		}
-		buftop=i;
-		bufbot=0;
-	}
-	ch=recvbuf[bufbot++];
-/*
-	if(buftop < sizeof(recvbuf)) {
-		i=conn_recv(recvbuf + buftop, sizeof(recvbuf) - buftop, 0);
-		if(i > 0)
-			buftop+=i;
-	}
- */
-/*	lprintf(LOG_DEBUG,"RX: %02X", ch); */
-	return(ch);
-}
-
-void purge_recv(void)
-{
-	unsigned count=0;
 
-	lprintf(LOG_NOTICE,"Purging receive buffer...");
-	YIELD();
-	while(recv_byte(NULL,0) >= 0) {
-		YIELD();
-		count++;
-	}
-	lprintf(LOG_NOTICE,"%u bytes purged",count);
+	if(conn_recv(&ch, sizeof(ch), timeout*1000))
+		return(ch);
+	return(-1);
 }
 
 #if defined(__BORLANDC__)
@@ -422,13 +380,7 @@ void purge_recv(void)
 #endif
 BOOL data_waiting(void* unused, unsigned timeout)
 {
-	BOOL rd;
-
-	if(bufbot < buftop)
-		return(TRUE);
-	if(!socket_check(conn_socket,&rd,NULL,timeout))
-		return(FALSE);
-	return(rd);
+	return(conn_data_waiting());
 }
 
 void draw_transfer_window(char* title)
@@ -544,22 +496,6 @@ void erase_transfer_window(void) {
 	_setcursortype(_NORMALCURSOR);
 }
 
-static void binary_mode_on(struct bbslist *bbs)
-{
-	if(bbs->conn_type == CONN_TYPE_TELNET) {
-		request_telnet_opt(TELNET_DO,TELNET_BINARY_TX);
-		request_telnet_opt(TELNET_WILL,TELNET_BINARY_TX);
-	}
-}
-
-static void binary_mode_off(struct bbslist *bbs)
-{
-	if(bbs->conn_type == CONN_TYPE_TELNET) {
-		request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
-		request_telnet_opt(TELNET_WONT,TELNET_BINARY_TX);
-	}
-}
-
 void ascii_upload(FILE *fp);
 void zmodem_upload(struct bbslist *bbs, FILE *fp, char *path);
 
@@ -629,9 +565,7 @@ void begin_upload(struct bbslist *bbs, BOOL autozm)
 #endif
 static BOOL is_connected(void* unused)
 {
-	if(bufbot < buftop)
-		return(TRUE);
-	return conn_is_connected();
+	return(conn_connected());
 }
 
 #ifdef GUTS_BUILTIN
@@ -873,7 +807,7 @@ void zmodem_upload(struct bbslist *bbs, FILE *fp, char *path)
 
 	zmodem_mode=ZMODEM_MODE_SEND;
 
-	binary_mode_on(bbs);
+	conn_binary_mode_on();
 	zmodem_init(&zm
 		,/* cbdata */&zm
 		,lputs, zmodem_progress
@@ -895,7 +829,7 @@ void zmodem_upload(struct bbslist *bbs, FILE *fp, char *path)
 
 	fclose(fp);
 
-	binary_mode_off(bbs);
+	conn_binary_mode_off();
 	lprintf(LOG_NOTICE,"Hit any key to continue...");
 	getch();
 
@@ -910,14 +844,11 @@ void zmodem_download(struct bbslist *bbs)
 
 	if(safe_mode)
 		return;
-#if 0
-	bufbot=buftop=0;	/* purge our receive buffer */
-#endif
 	draw_transfer_window("Zmodem Download");
 
 	zmodem_mode=ZMODEM_MODE_RECV;
 
-	binary_mode_on(bbs);
+	conn_binary_mode_on();
 	zmodem_init(&zm
 		,/* cbdata */&zm
 		,lputs, zmodem_progress
@@ -928,15 +859,10 @@ void zmodem_download(struct bbslist *bbs)
 
 	files_received=zmodem_recv_files(&zm,bbs->dldir,&bytes_received);
 
-#if 0
-	if(zm.local_abort)
-		purge_recv();
-#endif
-
 	if(files_received>1)
 		lprintf(LOG_INFO,"Received %u files (%lu bytes) successfully", files_received, bytes_received);
 
-	binary_mode_off(bbs);
+	conn_binary_mode_off();
 	lprintf(LOG_NOTICE,"Hit any key to continue...");
 	getch();
 
@@ -1113,11 +1039,7 @@ void capture_control(struct bbslist *bbs)
 BOOL doterm(struct bbslist *bbs)
 {
 	unsigned char ch[2];
-#ifdef GUTS_BUILTIN
-	unsigned char prn[1024];
-#else
-	unsigned char prn[BUFSIZE];
-#endif
+	unsigned char prn[ANSI_REPLY_BUFSIZE];
 	int	key;
 	int i,j;
 	unsigned char *p;
@@ -1140,7 +1062,7 @@ BOOL doterm(struct bbslist *bbs)
 
 	speed = bbs->bpsrate;
 	log_level = bbs->xfer_loglevel;
-	telnet_log_level = bbs->telnet_loglevel;
+	conn_api.log_level = bbs->telnet_loglevel;
 	ciomouse_setevents(0);
 	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START);
 	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE);
@@ -1168,7 +1090,7 @@ BOOL doterm(struct bbslist *bbs)
 		if(speed)
 			thischar=xp_timer();
 
-		while((bufbot < buftop) || (!socket_check(conn_socket,&rd,NULL,0)) || rd) {
+		while(conn_data_waiting() || !conn_connected()) {
 			if(!speed || thischar < lastchar /* Wrapped */ || thischar >= nextchar) {
 				/* Get remote input */
 				inch=recv_byte(NULL, 0);
@@ -1177,7 +1099,7 @@ BOOL doterm(struct bbslist *bbs)
 					update_status(bbs, speed);
 				switch(inch) {
 					case -1:
-						if(!is_connected(NULL)) {
+						if(!conn_connected()) {
 							uifcmsg("Disconnected","`Disconnected`\n\nRemote host dropped connection");
 							cterm_write("\x0c",1,NULL,0,NULL);	/* Clear screen into scrollback */
 							scrollback_lines=cterm.backpos;