about summary refs log tree commit diff
diff options
context:
space:
mode:
authorClint Adams <clint@users.sourceforge.net>2001-09-08 21:09:55 +0000
committerClint Adams <clint@users.sourceforge.net>2001-09-08 21:09:55 +0000
commitb554563ece82a257b02ca1c96e6783fa155fd0ea (patch)
treef313216867ff32bc6126784d05568c0a542cca5f
parent199bccab75dc3dca51418bb958d9a083d5c05130 (diff)
downloadzsh-b554563ece82a257b02ca1c96e6783fa155fd0ea.tar.gz
zsh-b554563ece82a257b02ca1c96e6783fa155fd0ea.tar.xz
zsh-b554563ece82a257b02ca1c96e6783fa155fd0ea.zip
15762: introduce ztcp builtin
-rw-r--r--ChangeLog6
-rw-r--r--Src/Modules/tcp.c308
-rw-r--r--Src/Modules/tcp.h20
-rw-r--r--Src/Modules/tcp.mdd1
-rw-r--r--Src/Modules/zftp.c104
5 files changed, 331 insertions, 108 deletions
diff --git a/ChangeLog b/ChangeLog
index 6162a97e3..93af34f88 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2001-09-08  Clint Adams  <clint@zsh.org>
+
+	* 15762: Src/Modules/tcp.c, Src/Modules/tcp.h,
+	Src/Modules/tcp.mdd, Src/Modules/zftp.c:
+	introduce ztcp builtin.
+
 2001-09-06  Clint Adams  <clint@zsh.org>
 
 	* 15740: Completion/compaudit: don't whine about
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index 7bd59fcce..eb2d49f0b 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -1,5 +1,5 @@
 /*
- * tcp.c - builtin FTP client
+ * tcp.c - TCP module
  *
  * This file is part of zsh, the Z shell.
  *
@@ -89,16 +89,16 @@ int h_errno;
 mod_export char const *
 zsh_inet_ntop(int af, void const *cp, char *buf, size_t len)
 {       
-        if(af != AF_INET) {
-                errno = EAFNOSUPPORT;
-                return NULL;
-        } 
-        if(len < INET_ADDRSTRLEN) {
-                errno = ENOSPC;
-                return NULL;
-        }
-        strcpy(buf, inet_ntoa(*(struct in_addr *)cp));
-        return buf;
+    if(af != AF_INET) {
+	errno = EAFNOSUPPORT;
+	return NULL;
+    } 
+    if(len < INET_ADDRSTRLEN) {
+	errno = ENOSPC;
+	return NULL;
+    }
+    strcpy(buf, inet_ntoa(*(struct in_addr *)cp));
+    return buf;
 }
 
 /**/
@@ -139,11 +139,11 @@ mod_export int zsh_inet_aton(char const *src, struct in_addr *dst)
 mod_export int
 zsh_inet_pton(int af, char const *src, void *dst)
 {
-        if(af != AF_INET) {
-                errno = EAFNOSUPPORT;
-                return -1;
-        }
-        return !!zsh_inet_aton(src, dst);
+    if(af != AF_INET) {
+	errno = EAFNOSUPPORT;
+	return -1;
+    }
+    return !!zsh_inet_aton(src, dst);
 }
 
 #else /* !HAVE_INET_PTON */
@@ -163,11 +163,11 @@ zsh_inet_pton(int af, char const *src, void *dst)
 mod_export struct hostent *
 zsh_gethostbyname2(char const *name, int af)
 {
-	if(af != AF_INET) {
-		h_errno = NO_RECOVERY;
-		return NULL;
-	}
-	return gethostbyname(name);
+    if(af != AF_INET) {
+	h_errno = NO_RECOVERY;
+	return NULL;
+    }
+    return gethostbyname(name);
 }
 
 /**/
@@ -187,28 +187,28 @@ zsh_gethostbyname2(char const *name, int af)
 mod_export struct hostent *
 zsh_getipnodebyname(char const *name, int af, int flags, int *errorp)
 {
-	static struct hostent ahe;
-	static char nbuf[16];
-	static char *addrlist[] = { nbuf, NULL };
+    static struct hostent ahe;
+    static char nbuf[16];
+    static char *addrlist[] = { nbuf, NULL };
 # ifdef SUPPORT_IPV6
-	static char pbuf[INET6_ADDRSTRLEN];
+    static char pbuf[INET6_ADDRSTRLEN];
 # else
-	static char pbuf[INET_ADDRSTRLEN];
+    static char pbuf[INET_ADDRSTRLEN];
 # endif
-	struct hostent *he;
-	if(zsh_inet_pton(af, name, nbuf) == 1) {
-		zsh_inet_ntop(af, nbuf, pbuf, sizeof(pbuf));
-		ahe.h_name = pbuf;
-		ahe.h_aliases = addrlist+1;
-		ahe.h_addrtype = af;
-		ahe.h_length = (af == AF_INET) ? 4 : 16;
-		ahe.h_addr_list = addrlist;
-		return &ahe;
-	}
-	he = zsh_gethostbyname2(name, af);
-	if(!he)
-		*errorp = h_errno;
-	return he;
+    struct hostent *he;
+    if(zsh_inet_pton(af, name, nbuf) == 1) {
+	zsh_inet_ntop(af, nbuf, pbuf, sizeof(pbuf));
+	ahe.h_name = pbuf;
+	ahe.h_aliases = addrlist+1;
+	ahe.h_addrtype = af;
+	ahe.h_length = (af == AF_INET) ? 4 : 16;
+	ahe.h_addr_list = addrlist;
+	return &ahe;
+    }
+    he = zsh_gethostbyname2(name, af);
+    if(!he)
+	*errorp = h_errno;
+    return he;
 }
 
 /**/
@@ -226,29 +226,130 @@ freehostent(struct hostent *ptr)
 /**/
 #endif /* !HAVE_GETIPNODEBYNAME */
 
+Tcp_session ztcp_head = NULL, ztcp_tail = NULL;
+
+static Tcp_session
+zts_head(void)
+{
+    return ztcp_head;
+}
+
+static Tcp_session
+zts_next(Tcp_session cur)
+{
+    return cur ? cur->next : NULL;
+}
+
+/* "allocate" a tcp_session */
+static Tcp_session
+zts_alloc(int ztflags)
+{
+    Tcp_session sess;
+
+    sess = (Tcp_session)zcalloc(sizeof(struct tcp_session));
+    if(!sess) return NULL;
+    sess->fd=-1;
+    sess->next=NULL;
+    sess->flags=ztflags;
+
+    if(!zts_head()) {
+	ztcp_head = ztcp_tail = sess;
+    }
+    else {
+	ztcp_tail->next = sess;
+    }
+    return sess;
+}
+
 /**/
-mod_export int
-tcp_socket(int domain, int type, int protocol, Tcp_session sess)
+mod_export Tcp_session
+tcp_socket(int domain, int type, int protocol, int ztflags)
 {
+    Tcp_session sess;
+
+    sess = zts_alloc(ztflags);
+    if(!sess) return NULL;
+
     sess->fd = socket(domain, type, protocol);
-    return sess->fd;
+    return sess;
+}
+
+static int
+zts_delete(Tcp_session sess)
+{
+    Tcp_session tsess;
+
+    tsess = zts_head();
+
+    if(tsess == sess)
+    {
+	ztcp_head = sess->next;
+	free(sess);
+	return 0;
+    }
+
+    while((tsess->next != sess) && (tsess->next))
+    {
+	tsess = zts_next(tsess);
+    }
+
+    if(!tsess->next) return 1;
+
+    tsess->next = tsess->next->next;
+    free(tsess->next);
+    return 0;
+
+}
+
+static Tcp_session
+zts_byfd(int fd)
+{
+    Tcp_session tsess;
+
+    tsess = zts_head();
+
+    do {
+	if(tsess->fd == fd)
+	    return tsess;
+
+	tsess = zts_next(tsess);
+    }
+    while(tsess != NULL);
+
+    return NULL;
 }
 
 static void
 tcp_cleanup(void)
 {
+    Tcp_session sess, prev;
+    
+    for(sess = zts_head(); sess != NULL; sess = zts_next(prev))
+    {
+	prev = sess;
+	tcp_close(sess);
+	zts_delete(sess);
+    }
 }
 
 /**/
 mod_export int
 tcp_close(Tcp_session sess)
 {
-    if(!close(sess->fd))
-    {
-	sess->fd = -1;
+    int err;
+    
+    if(sess->fd != -1)
+    {  
+	err = close(sess->fd);
+	if(err)
+	{
+	    zwarn("connection close failed: %e", NULL, errno);
+	    return -1;
+	}
 	return 0;
     }
-       else return -1;
+
+    return -1;
 }
 
 /**/
@@ -270,12 +371,122 @@ tcp_connect(Tcp_session sess, char *addrp, struct hostent *zhost, int d_port)
     {
 	memcpy(&(sess->peer.in.sin_addr), addrp, zhost->h_length);
 	sess->peer.in.sin_port = d_port;
+	sess->peer.a.sa_family = zhost->h_addrtype;
 	salen = sizeof(struct sockaddr_in);
     }
 
     return connect(sess->fd, (struct sockaddr *)&(sess->peer), salen);
 }
 
+static int
+bin_ztcp(char *nam, char **args, char *ops, int func)
+{
+    int herrno, err=1, destport, force=0, len;
+    char **addrp, *desthost;
+    struct hostent *zthost = NULL;
+    Tcp_session sess;
+
+    if (ops['f'])
+	force=1;
+    
+    if (ops['c']) {
+	if (!args[0]) {
+	    tcp_cleanup();
+	}
+	else {
+	    int targetfd = atoi(args[0]);
+	    sess = zts_byfd(targetfd);
+
+	    if(sess)
+	    {
+		if((sess->flags & ZTCP_ZFTP) && !force)
+		{
+		    zwarnnam(nam, "use -f to force closure of a zftp control connection", NULL, 0);
+		    return 1;
+		}
+		tcp_close(sess);
+		zts_delete(sess);
+		return 0;
+	    }
+	    else
+	    {
+		zwarnnam(nam, "fd not found in tcp table", NULL, 0);
+		return 1;
+	    }
+	}
+    }
+    else {
+	
+	if (!args[0]) {
+	    for(sess = zts_head(); sess != NULL; sess = zts_next(sess))
+	    {
+		if(sess->fd != -1)
+		{
+		    zthost = gethostbyaddr(&(sess->peer.in.sin_addr), sizeof(struct sockaddr_in), AF_INET);
+		    if(zthost) fprintf(shout, "%s:%d is on fd %d%s\n", zthost->h_name, ntohs(sess->peer.in.sin_port), sess->fd, (sess->flags & ZTCP_ZFTP) ? " ZFTP" : "");
+		    else fprintf(shout, "%s:%d is on fd %d%s\n", "UNKNOWN", sess->peer.in.sin_port, sess->fd, (sess->flags & ZTCP_ZFTP) ? " ZFTP" : "");
+		}
+	    }
+	    return 0;
+	}
+	else if (!args[1]) {
+	    destport = 23;
+	}
+	else {
+	    destport = atoi(args[1]);
+	}
+	
+	desthost = ztrdup(args[0]);
+	
+	zthost = zsh_getipnodebyname(desthost, AF_INET, 0, &herrno);
+	if (!zthost || errflag) {
+	    zwarnnam(nam, "host resolution failure: %s", desthost, 0);
+	    return 1;
+	}
+	
+	sess = tcp_socket(PF_INET, SOCK_STREAM, 0, 0);
+#ifdef SO_OOBINLINE
+	len = 1;
+	setsockopt(sess->fd, SOL_SOCKET, SO_OOBINLINE, (char *)&len, sizeof(len));
+#endif
+
+	if(!sess) {
+	    zwarnnam(nam, "unable to allocate a TCP session slot", NULL, 0);
+	    return 1;
+	}
+	if (sess->fd < 0) {
+	    zwarnnam(nam, "socket creation failed: %e", NULL, errno);
+	    zsfree(desthost);
+	    zts_delete(sess);
+	    return 1;
+	}
+	
+	for (addrp = zthost->h_addr_list; err && *addrp; addrp++) {
+	    if(zthost->h_length != 4)
+		zwarnnam(nam, "address length mismatch", NULL, 0);
+	    do {
+		err = tcp_connect(sess, *addrp, zthost, htons(destport));
+	    } while (err && errno == EINTR && !errflag);
+	}
+	
+	if(err)
+	    zwarnnam(nam, "connection failed: %e", NULL, errno);
+	else
+	{
+	    fprintf(shout, "%s:%d is now on fd %d\n", desthost, destport, sess->fd);
+	}
+	
+	zsfree(desthost);
+    }
+
+    return 0;
+    
+}
+
+static struct builtin bintab[] = {
+    BUILTIN("ztcp", 0, bin_ztcp, 0, 2, 0, "c", NULL),
+};
+
 /* The load/unload routines required by the zsh library interface */
 
 /**/
@@ -289,9 +500,10 @@ setup_(Module m)
 int
 boot_(Module m)
 {
-    return 0;
+    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
 }
 
+
 /**/
 int
 cleanup_(Module m)
diff --git a/Src/Modules/tcp.h b/Src/Modules/tcp.h
index 53e96761f..94efb255a 100644
--- a/Src/Modules/tcp.h
+++ b/Src/Modules/tcp.h
@@ -63,21 +63,25 @@
 #endif
 
 union tcp_sockaddr {
-	struct sockaddr a;
-	struct sockaddr_in in;
+    struct sockaddr a;
+    struct sockaddr_in in;
 #ifdef SUPPORT_IPV6
-	struct sockaddr_in6 in6;
+    struct sockaddr_in6 in6;
 #endif
 };
 
+typedef struct tcp_session *Tcp_session;
+
+#define ZTCP_ZFTP 16
+
 struct tcp_session {
-	int fd;				/* file descriptor */
-	union tcp_sockaddr sock;  	/* local address   */
-	union tcp_sockaddr peer;  	/* remote address  */
+    int fd;				/* file descriptor */
+    union tcp_sockaddr sock;  	/* local address   */
+    union tcp_sockaddr peer;  	/* remote address  */
+    Tcp_session next;
+    int flags;
 };
 
-typedef struct tcp_session *Tcp_session;
-
 #include "tcp.mdh"
 #include "tcp.pro"
 
diff --git a/Src/Modules/tcp.mdd b/Src/Modules/tcp.mdd
index 0c6a1729a..041d9138a 100644
--- a/Src/Modules/tcp.mdd
+++ b/Src/Modules/tcp.mdd
@@ -3,3 +3,4 @@ link=dynamic
 load=no
 
 objects="tcp.o"
+autobins="ztcp"
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 7e511c63e..db7afcbe7 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -75,10 +75,10 @@ int h_errno;
 #endif
 
 union zftp_sockaddr {
-	struct sockaddr a;
-	struct sockaddr_in in;
+    struct sockaddr a;
+    struct sockaddr_in in;
 #ifdef SUPPORT_IPV6
-	struct sockaddr_in6 in6;
+    struct sockaddr_in6 in6;
 #endif
 };
 
@@ -301,7 +301,7 @@ struct zftp_session {
     char **params;		/* parameters ordered as in zfparams */
     char **userparams;		/* user parameters set by zftp_params */
     FILE *cin;			/* control input file */
-    struct tcp_session control;	/* the control connection */
+    Tcp_session control;	/* the control connection */
     int dfd;			/* data connection */
     int has_size;		/* understands SIZE? */
     int has_mdtm;		/* understands MDTM? */
@@ -643,7 +643,7 @@ zfgetline(char *ln, int lnsize, int tmout)
 		cmdbuf[0] = (char)IAC;
 		cmdbuf[1] = (char)DONT;
 		cmdbuf[2] = ch;
-		write(zfsess->control.fd, cmdbuf, 3);
+		write(zfsess->control->fd, cmdbuf, 3);
 		continue;
 
 	    case DO:
@@ -653,7 +653,7 @@ zfgetline(char *ln, int lnsize, int tmout)
 		cmdbuf[0] = (char)IAC;
 		cmdbuf[1] = (char)WONT;
 		cmdbuf[2] = ch;
-		write(zfsess->control.fd, cmdbuf, 3);
+		write(zfsess->control->fd, cmdbuf, 3);
 		continue;
 
 	    case EOF:
@@ -703,7 +703,7 @@ zfgetmsg(void)
     char line[256], *ptr, *verbose;
     int stopit, printing = 0, tmout;
 
-    if (zfsess->control.fd == -1)
+    if ((zfsess->control && zfsess->control->fd == -1))
 	return 6;
     zsfree(lastmsg);
     lastmsg = NULL;
@@ -831,7 +831,7 @@ zfsendcmd(char *cmd)
      */
     int ret, tmout;
 
-    if (zfsess->control.fd == -1)
+    if ((zfsess->control && zfsess->control->fd == -1))
 	return 6;
     tmout = getiparam("ZFTP_TMOUT");
     if (setjmp(zfalrmbuf)) {
@@ -840,7 +840,7 @@ zfsendcmd(char *cmd)
 	return 6;
     }
     zfalarm(tmout);
-    ret = write(zfsess->control.fd, cmd, strlen(cmd));
+    ret = write(zfsess->control->fd, cmd, strlen(cmd));
     alarm(0);
 
     if (ret <= 0) {
@@ -874,7 +874,7 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
 	int err, salen;
 
 #ifdef SUPPORT_IPV6
-	if(zfsess->control.peer.a.sa_family == AF_INET6)
+	if(zfsess->control->peer.a.sa_family == AF_INET6)
 	    psv_cmd = "EPSV\r\n";
 	else
 #endif /* SUPPORT_IPV6 */
@@ -890,9 +890,9 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
 	    zfclosedata();
 	    return zfopendata(name, zdsockp, is_passivep);
 	}
-	zdsockp->a.sa_family = zfsess->control.peer.a.sa_family;
+	zdsockp->a.sa_family = zfsess->control->peer.a.sa_family;
 #ifdef SUPPORT_IPV6
-	if(zfsess->control.peer.a.sa_family == AF_INET6) {
+	if(zfsess->control->peer.a.sa_family == AF_INET6) {
 	    /* see RFC 2428 for explanation */
 	    char const *ptr, *end;
 	    char delim, portbuf[6], *pbp;
@@ -920,7 +920,7 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
 	    portnum = strtoul(portbuf, &pbp, 10);
 	    if(*pbp || portnum > 65535UL)
 		goto bad_epsv;
-	    *zdsockp = zfsess->control.peer;
+	    *zdsockp = zfsess->control->peer;
 	    zdsockp->in6.sin6_port = htons((unsigned)portnum);
 	    salen = sizeof(struct sockaddr_in6);
 	} else
@@ -980,7 +980,7 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
 	    return 1;
 	}
 
-	*zdsockp = zfsess->control.sock;
+	*zdsockp = zfsess->control->sock;
 #ifdef SUPPORT_IPV6
 	if(zdsockp->a.sa_family == AF_INET6) {
 	    zdsockp->in6.sin6_port = 0;	/* to be set by bind() */
@@ -1015,9 +1015,9 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
 	    /* see RFC 2428 for explanation */
 	    strcpy(portcmd, "EPRT |2|");
 	    zsh_inet_ntop(AF_INET6, &zdsockp->in6.sin6_addr,
-		portcmd+8, INET6_ADDRSTRLEN);
+			  portcmd+8, INET6_ADDRSTRLEN);
 	    sprintf(strchr(portcmd, 0), "|%u|\r\n",
-		(unsigned)ntohs(zdsockp->in6.sin6_port));
+		    (unsigned)ntohs(zdsockp->in6.sin6_port));
 	} else
 #endif /* SUPPORT_IPV6 */
 	{
@@ -1171,8 +1171,8 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize)
     }
 #endif
 #if defined(F_SETFD) && defined(FD_CLOEXEC)
-	/* If the shell execs a program, we don't want this fd left open. */
-	fcntl(zfsess->dfd, F_SETFD, FD_CLOEXEC);
+    /* If the shell execs a program, we don't want this fd left open. */
+    fcntl(zfsess->dfd, F_SETFD, FD_CLOEXEC);
 #endif
 
     return 0;
@@ -1655,8 +1655,8 @@ zfsenddata(char *name, int recv, int progress, off_t startat)
 
 	/* the following is black magic, as far as I'm concerned. */
 	/* what are we going to do if it fails?  not a lot, actually. */
-	send(zfsess->control.fd, (char *)msg, 3, 0);
-	send(zfsess->control.fd, (char *)msg+3, 1, MSG_OOB);
+	send(zfsess->control->fd, (char *)msg, 3, 0);
+	send(zfsess->control->fd, (char *)msg+3, 1, MSG_OOB);
 
 	zfsendcmd("ABOR\r\n");
 	if (lastcode == 226) {
@@ -1718,7 +1718,7 @@ zftp_open(char *name, char **args, int flags)
      * Probably this is the safest thing to do.  It's possible
      * a `QUIT' will hang, though.
      */
-    if (zfsess->control.fd != -1)
+    if ((zfsess->control && zfsess->control->fd != -1))
 	zfclose(0);
 
     /* this is going to give 0.  why bother? */
@@ -1757,12 +1757,12 @@ zftp_open(char *name, char **args, int flags)
 # define SUCCEEDED() break
 # define FAILED() if(af == AF_INET) { } else continue
 #else
-    af = AF_INET;
+	af = AF_INET;
 # define SUCCEEDED() do { } while(0)
 # define FAILED() do { } while(0)
 #endif
     {
-    	zhostp = zsh_getipnodebyname(args[0], af, 0, &herrno);
+	zhostp = zsh_getipnodebyname(args[0], af, 0, &herrno);
 	if (!zhostp || errflag) {
 	    /* should use herror() here if available, but maybe
 	     * needs configure test. on AIX it's present but not
@@ -1777,7 +1777,6 @@ zftp_open(char *name, char **args, int flags)
 	}
 	zfsetparam("ZFTP_HOST", ztrdup(zhostp->h_name), ZFPM_READONLY);
 
-	zfsess->control.peer.a.sa_family = af;
 #ifdef SUPPORT_IPV6
 	if(af == AF_INET6) {
 	    hlen = 16;
@@ -1787,8 +1786,9 @@ zftp_open(char *name, char **args, int flags)
 	    hlen = 4;
 	}
 
-	tcp_socket(af, SOCK_STREAM, 0, &(zfsess->control));
-	if (zfsess->control.fd < 0) {
+	zfsess->control = tcp_socket(af, SOCK_STREAM, 0, ZTCP_ZFTP);
+
+	if (!(zfsess->control) || (zfsess->control->fd < 0)) {
 	    freehostent(zhostp);
 	    zfunsetparam("ZFTP_HOST");
 	    FAILED();
@@ -1811,7 +1811,7 @@ zftp_open(char *name, char **args, int flags)
 	    if(hlen != zhostp->h_length)
 		zwarnnam(name, "address length mismatch", NULL, 0);
 	    do {
-		err = tcp_connect(&(zfsess->control), *addrp, zhostp, zservp->s_port);
+		err = tcp_connect(zfsess->control, *addrp, zhostp, zservp->s_port);
 	    } while (err && errno == EINTR && !errflag);
 	    /* you can check whether it's worth retrying here */
 	}
@@ -1846,15 +1846,15 @@ zftp_open(char *name, char **args, int flags)
      * Move the fd out of the user-visible range.  We need to do
      * this after the connect() on some systems.
      */
-    zfsess->control.fd = zfmovefd(zfsess->control.fd);
+    zfsess->control->fd = zfmovefd(zfsess->control->fd);
 
 #if defined(F_SETFD) && defined(FD_CLOEXEC)
     /* If the shell execs a program, we don't want this fd left open. */
-    fcntl(zfsess->control.fd, F_SETFD, FD_CLOEXEC);
+    fcntl(zfsess->control->fd, F_SETFD, FD_CLOEXEC);
 #endif
 
-    len = sizeof(zfsess->control.sock);
-    if (getsockname(zfsess->control.fd, (struct sockaddr *)&zfsess->control.sock, &len) < 0) {
+    len = sizeof(zfsess->control->sock);
+    if (getsockname(zfsess->control->fd, (struct sockaddr *)&zfsess->control->sock, &len) < 0) {
 	zwarnnam(name, "getsockname failed: %e", NULL, errno);
 	zfclose(0);
 	return 1;
@@ -1866,20 +1866,20 @@ zftp_open(char *name, char **args, int flags)
      * do clever things with SIGURG.
      */
     len = 1;
-    setsockopt(zfsess->control.fd, SOL_SOCKET, SO_OOBINLINE,
+    setsockopt(zfsess->control->fd, SOL_SOCKET, SO_OOBINLINE,
 	       (char *)&len, sizeof(len));
 #endif
 #if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
     /* for control connection we want low delay.  please don't laugh. */
     len = IPTOS_LOWDELAY;
-    setsockopt(zfsess->control.fd, IPPROTO_IP, IP_TOS, (char *)&len, sizeof(len));
+    setsockopt(zfsess->control->fd, IPPROTO_IP, IP_TOS, (char *)&len, sizeof(len));
 #endif
 
     /*
      * We use stdio with line buffering for convenience on input.
      * On output, we can just dump a complete message to the fd via write().
      */
-    zfsess->cin = fdopen(zfsess->control.fd, "r");
+    zfsess->cin = fdopen(zfsess->control->fd, "r");
 
     if (!zfsess->cin) {
 	zwarnnam(name, "file handling error", NULL, 0);
@@ -1923,17 +1923,17 @@ zftp_open(char *name, char **args, int flags)
 	unlink(fname);
     }
 
-    if (zfsess->control.fd == -1) {
+    if (zfsess->control->fd == -1) {
 	/* final paranoid check */
 	return 1;
     }
 	
     zfsetparam("ZFTP_MODE", ztrdup("S"), ZFPM_READONLY);
     /* if remaining arguments, use them to log in. */
-    if (zfsess->control.fd > -1 && *++args)
+    if (zfsess->control->fd > -1 && *++args)
 	return zftp_login(name, args, flags);
     /* if something wayward happened, connection was already closed */
-    return zfsess->control.fd == -1;
+    return zfsess->control->fd == -1;
 }
 
 /*
@@ -1995,7 +1995,7 @@ zfgetinfo(char *prompt, int noecho)
 	/* '\n' didn't get echoed */
 	fputc('\n', stdout);
 	fflush(stdout);
-    	settyinfo(&shttyinfo);
+	settyinfo(&shttyinfo);
     }
 
     return strret;
@@ -2127,7 +2127,7 @@ zftp_login(char *name, char **args, int flags)
     }
 
     zsfree(ucmd);
-    if (zfsess->control.fd == -1)
+    if (zfsess->control->fd == -1)
 	return 1;
     if (stopit == 2 || (lastcode != 230 && lastcode != 202)) {
 	zwarnnam(name, "login failed", NULL, 0);
@@ -2206,7 +2206,7 @@ zftp_test(char *name, char **args, int flags)
     struct timeval tv;
 # endif /* HAVE_POLL */
 
-    if (zfsess->control.fd == -1)
+    if (zfsess->control->fd == -1)
 	return 1;
 
 # ifdef HAVE_POLL
@@ -2214,7 +2214,7 @@ zftp_test(char *name, char **args, int flags)
     /* safety first, though I think POLLIN is more common */
 #   define POLLIN POLLNORM
 #  endif /* HAVE_POLL */
-    pfd.fd = zfsess->control.fd;
+    pfd.fd = zfsess->control->fd;
     pfd.events = POLLIN;
     if ((ret = poll(&pfd, 1, 0)) < 0 && errno != EINTR && errno != EAGAIN)
 	zfclose(0);
@@ -2224,10 +2224,10 @@ zftp_test(char *name, char **args, int flags)
     }
 # else
     FD_ZERO(&f);
-    FD_SET(zfsess->control.fd, &f);
+    FD_SET(zfsess->control->fd, &f);
     tv.tv_sec = 0;
     tv.tv_usec = 0;
-    if ((ret = select(zfsess->control.fd +1, (SELECT_ARG_2_T) &f,
+    if ((ret = select(zfsess->control->fd +1, (SELECT_ARG_2_T) &f,
 		      NULL, NULL, &tv)) < 0
 	&& errno != EINTR)
 	zfclose(0);
@@ -2236,8 +2236,8 @@ zftp_test(char *name, char **args, int flags)
 	zfgetmsg();
     }
 # endif /* HAVE_POLL */
-    /* if we now have zfsess->control.fd == -1, then we've just been dumped out. */
-    return (zfsess->control.fd == -1) ? 2 : 0;
+    /* if we now have zfsess->control->fd == -1, then we've just been dumped out. */
+    return (zfsess->control->fd == -1) ? 2 : 0;
 #else
     zfwarnnam(name, "not supported on this system.", NULL, 0);
     return 3;
@@ -2659,7 +2659,7 @@ zfclose(int leaveparams)
     char **aptr;
     Eprog prog;
 
-    if (zfsess->control.fd == -1)
+    if (zfsess->control->fd == -1)
 	return;
 
     zfclosing = 1;
@@ -2675,9 +2675,9 @@ zfclose(int leaveparams)
 	fclose(zfsess->cin);
 	zfsess->cin = NULL;
     }
-    if (zfsess->control.fd != -1) {
+    if (zfsess->control->fd != -1) {
 	zfnopen--;
-	tcp_close(&(zfsess->control));
+	tcp_close(zfsess->control);
     }
 
     if (zfstatfd != -1) {
@@ -2750,7 +2750,7 @@ newsession(char *nm)
     if (!nptr) {
 	zfsess = (Zftp_session) zcalloc(sizeof(struct zftp_session));
 	zfsess->name = ztrdup(nm);
-	zfsess->control.fd = zfsess->dfd = -1;
+	zfsess->dfd = -1;
 	zfsess->params = (char **) zcalloc(sizeof(zfparams));
 	zaddlinknode(zfsessions, zfsess);
 
@@ -2965,7 +2965,7 @@ bin_zftp(char *name, char **args, char *ops, int func)
 	int oldstatus = zfstatusp[zfsessno];
 	lseek(zfstatfd, 0, 0);
 	read(zfstatfd, (char *)zfstatusp, sizeof(int)*zfsesscnt);
-	if (zfsess->control.fd != -1 && (zfstatusp[zfsessno] & ZFST_CLOS)) {
+	if ((zfsess->control && zfsess->control->fd != -1) && (zfstatusp[zfsessno] & ZFST_CLOS)) {
 	    /* got closed in subshell without us knowing */
 	    zcfinish = 2;
 	    zfclose(0);
@@ -2986,7 +2986,7 @@ bin_zftp(char *name, char **args, char *ops, int func)
 	}
     }
 #if defined(HAVE_SELECT) || defined (HAVE_POLL)
-    if (zfsess->control.fd != -1 && !(zptr->flags & (ZFTP_TEST|ZFTP_SESS))) {
+    if ((zfsess->control && zfsess->control->fd != -1) && !(zptr->flags & (ZFTP_TEST|ZFTP_SESS))) {
 	/*
 	 * Test the connection for a bad fd or incoming message, but
 	 * only if the connection was last heard of open, and
@@ -2996,7 +2996,7 @@ bin_zftp(char *name, char **args, char *ops, int func)
 	ret = zftp_test("zftp test", NULL, 0);
     }
 #endif
-    if ((zptr->flags & ZFTP_CONN) && zfsess->control.fd == -1) {
+    if ((zptr->flags & ZFTP_CONN) && (zfsess->control && zfsess->control->fd == -1)) {
 	if (ret != 2) {
 	    /*
 	     * with ret == 2, we just got dumped out in the test,