about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/tcp.c271
-rw-r--r--Src/Modules/tcp.h67
-rw-r--r--Src/Modules/tcp.mdd5
-rw-r--r--Src/Modules/zftp.c259
-rw-r--r--Src/Modules/zftp.mdd2
5 files changed, 399 insertions, 205 deletions
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
new file mode 100644
index 000000000..744343d67
--- /dev/null
+++ b/Src/Modules/tcp.c
@@ -0,0 +1,271 @@
+/*
+ * tcp.c - builtin FTP client
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1998-2001 Peter Stephenson
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Peter Stephenson or the Zsh Development
+ * Group be liable to any party for direct, indirect, special, incidental,
+ * or consequential damages arising out of the use of this software and
+ * its documentation, even if Peter Stephenson, and the Zsh
+ * Development Group have been advised of the possibility of such damage.
+ *
+ * Peter Stephenson and the Zsh Development Group specifically
+ * disclaim any warranties, including, but not limited to, the implied
+ * warranties of merchantability and fitness for a particular purpose.  The
+ * software provided hereunder is on an "as is" basis, and Peter Stephenson
+ * and the Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+/*
+ * We need to include the zsh headers later to avoid clashes with
+ * the definitions on some systems, however we need the configuration
+ * file to decide whether we can include netinet/in_systm.h, which
+ * doesn't exist on cygwin.
+ */
+#include "tcp.h"
+
+/*
+ * For some reason, configure doesn't always detect netinet/in_systm.h.
+ * On some systems, including linux, this seems to be because gcc is
+ * throwing up a warning message about the redefinition of
+ * __USE_LARGEFILE.  This means the problem is somewhere in the
+ * header files where we can't get at it.  For now, revert to
+ * not including this file only on systems where we know it's missing.
+ * Currently this is just cygwin.
+ */
+#ifndef __CYGWIN__
+# include <netinet/in_systm.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+/* it's a TELNET based protocol, but don't think I like doing this */
+#include <arpa/telnet.h>
+
+/*
+ * We use poll() in preference to select because some subset of manuals says
+ * that's the thing to do, plus it's a bit less fiddly.  I don't actually
+ * have access to a system with poll but not select, however, though
+ * both bits of the code have been tested on a machine with both.
+ */
+#ifdef HAVE_POLL_H
+# include <poll.h>
+#endif
+#if defined(HAVE_POLL) && !defined(POLLIN) && !defined(POLLNORM)
+# undef HAVE_POLL
+#endif
+
+#ifdef USE_LOCAL_H_ERRNO
+int h_errno;
+#endif
+
+/* We use the RFC 2553 interfaces.  If the functions don't exist in the library,
+   simulate them. */
+
+#ifndef INET_ADDRSTRLEN
+# define INET_ADDRSTRLEN 16
+#endif
+
+#ifndef INET6_ADDRSTRLEN
+# define INET6_ADDRSTRLEN 46
+#endif
+
+/**/
+#ifndef HAVE_INET_NTOP
+
+/**/
+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;
+}
+
+/**/
+#else /* !HAVE_INET_NTOP */
+
+/**/
+# define zsh_inet_ntop inet_ntop
+
+/**/
+#endif /* !HAVE_INET_NTOP */
+
+/**/
+#ifndef HAVE_INET_PTON
+
+/**/
+# ifndef HAVE_INET_ATON
+
+#  ifndef INADDR_NONE
+#   define INADDR_NONE 0xffffffffUL
+#  endif
+
+/**/
+mod_export int zsh_inet_aton(char const *src, struct in_addr *dst)
+{
+    return (dst->s_addr = inet_addr(src)) != INADDR_NONE;
+}
+
+/**/
+#else /* !HAVE_INET_ATON */
+
+/**/
+# define zsh_inet_aton inet_aton
+
+/**/
+# endif /* !HAVE_INET_ATON */
+
+/**/
+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);
+}
+
+#else /* !HAVE_INET_PTON */
+
+# define zsh_inet_pton inet_pton
+
+/**/
+#endif /* !HAVE_INET_PTON */
+
+/**/
+#ifndef HAVE_GETIPNODEBYNAME
+
+/**/
+# ifndef HAVE_GETHOSTBYNAME2
+
+/**/
+mod_export struct hostent *
+zsh_gethostbyname2(char const *name, int af)
+{
+	if(af != AF_INET) {
+		h_errno = NO_RECOVERY;
+		return NULL;
+	}
+	return gethostbyname(name);
+}
+
+/**/
+#else /* !HAVE_GETHOSTBYNAME2 */
+
+/**/
+# define zsh_gethostbyname2 gethostbyname2
+
+/**/
+# endif /* !HAVE_GETHOSTBYNAME2 */
+
+/* note: this is not a complete implementation.  If ignores the flags,
+   and does not provide the memory allocation of the standard interface.
+   Each returned structure will overwrite the previous one. */
+
+/**/
+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 };
+# ifdef SUPPORT_IPV6
+	static char pbuf[INET6_ADDRSTRLEN];
+# else
+	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;
+}
+
+/**/
+mod_export void
+freehostent(struct hostent *ptr)
+{
+}
+
+/**/
+#else /* !HAVE_GETIPNODEBYNAME */
+
+/**/
+# define zsh_getipnodebyname getipnodebyname
+
+/**/
+#endif /* !HAVE_GETIPNODEBYNAME */
+
+/**/
+mod_export int
+tcp_socket(int domain, int type, int protocol, Tcp_session sess)
+{
+	sess->fd = socket(domain, type, protocol);
+	return sess->fd;
+}
+
+static void
+tcp_cleanup(void)
+{
+}
+
+/* The load/unload routines required by the zsh library interface */
+
+/**/
+int
+setup_(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+    tcp_cleanup();
+    return 0;
+}
+
+/**/
+int
+finish_(Module m)
+{
+    return 0;
+}
diff --git a/Src/Modules/tcp.h b/Src/Modules/tcp.h
new file mode 100644
index 000000000..edb2119ad
--- /dev/null
+++ b/Src/Modules/tcp.h
@@ -0,0 +1,67 @@
+/*
+ * tcp.h - builtin FTP client
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1998-2001 Peter Stephenson
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Peter Stephenson or the Zsh Development
+ * Group be liable to any party for direct, indirect, special, incidental,
+ * or consequential damages arising out of the use of this software and
+ * its documentation, even if Peter Stephenson, and the Zsh
+ * Development Group have been advised of the possibility of such damage.
+ *
+ * Peter Stephenson and the Zsh Development Group specifically
+ * disclaim any warranties, including, but not limited to, the implied
+ * warranties of merchantability and fitness for a particular purpose.  The
+ * software provided hereunder is on an "as is" basis, and Peter Stephenson
+ * and the Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "../../config.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+/* Is IPv6 supported by the library? */
+
+#if defined(AF_INET6) && defined(IN6ADDR_LOOPBACK_INIT) \
+	&& defined(HAVE_INET_NTOP) && defined(HAVE_INET_PTON)
+# define SUPPORT_IPV6 1
+#endif
+
+union tcp_sockaddr {
+	struct sockaddr a;
+	struct sockaddr_in in;
+#ifdef SUPPORT_IPV6
+	struct sockaddr_in6 in6;
+#endif
+};
+
+struct tcp_session {
+	int fd;				/* file descriptor */
+	union tcp_sockaddr sock;  	/* local address   */
+	union tcp_sockaddr peer;  	/* remote address  */
+};
+
+typedef struct tcp_session *Tcp_session;
+
+#include "tcp.mdh"
+#include "tcp.pro"
+
+#ifndef INET_ADDRSTRLEN
+# define INET_ADDRSTRLEN 16
+#endif
+
+#ifndef INET6_ADDRSTRLEN
+# define INET6_ADDRSTRLEN 46
+#endif
diff --git a/Src/Modules/tcp.mdd b/Src/Modules/tcp.mdd
new file mode 100644
index 000000000..576a573ac
--- /dev/null
+++ b/Src/Modules/tcp.mdd
@@ -0,0 +1,5 @@
+name=zsh/tcp
+link=dynamic
+load=no
+
+objects="tcp.o"
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 0f70f9417..46f30da74 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -46,7 +46,6 @@ struct hostent;
 struct in_addr;
 struct sockaddr_in;
 struct sockaddr_in6;
-union zftp_sockaddr;
 struct zftp_session;
 typedef struct zftp_session *Zftp_session;
 
@@ -77,6 +76,7 @@ typedef struct zftp_session *Zftp_session;
 #include <netinet/ip.h>
 #include <arpa/inet.h>
 
+#include "tcp.h"
 #include "zftp.mdh"
 #include "zftp.pro"
 
@@ -96,11 +96,9 @@ typedef struct zftp_session *Zftp_session;
 # undef HAVE_POLL
 #endif
 
-/* Is IPv6 supported by the library? */
 
-#if defined(AF_INET6) && defined(IN6ADDR_LOOPBACK_INIT) \
-	&& defined(HAVE_INET_NTOP) && defined(HAVE_INET_PTON)
-# define SUPPORT_IPV6 1
+#ifdef USE_LOCAL_H_ERRNO
+int h_errno;
 #endif
 
 union zftp_sockaddr {
@@ -115,153 +113,6 @@ union zftp_sockaddr {
 int h_errno;
 #endif
 
-/* We use the RFC 2553 interfaces.  If the functions don't exist in the library,
-   simulate them. */
-
-#ifndef INET_ADDRSTRLEN
-# define INET_ADDRSTRLEN 16
-#endif
-
-#ifndef INET6_ADDRSTRLEN
-# define INET6_ADDRSTRLEN 46
-#endif
-
-/**/
-#ifndef HAVE_INET_NTOP
-
-/**/
-static 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;
-}
-
-#else /* !HAVE_INET_NTOP */
-
-# define zsh_inet_ntop inet_ntop
-
-/**/
-#endif /* !HAVE_INET_NTOP */
-
-/**/
-#ifndef HAVE_INET_PTON
-
-/**/
-# ifndef HAVE_INET_ATON
-
-#  ifndef INADDR_NONE
-#   define INADDR_NONE 0xffffffffUL
-#  endif
-
-/**/
-static int zsh_inet_aton(char const *src, struct in_addr *dst)
-{
-    return (dst->s_addr = inet_addr(src)) != INADDR_NONE;
-}
-
-#else /* !HAVE_INET_ATON */
-
-# define zsh_inet_aton inet_aton
-
-/**/
-# endif /* !HAVE_INET_ATON */
-
-/**/
-static 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);
-}
-
-#else /* !HAVE_INET_PTON */
-
-# define zsh_inet_pton inet_pton
-
-/**/
-#endif /* !HAVE_INET_PTON */
-
-/**/
-#ifndef HAVE_GETIPNODEBYNAME
-
-/**/
-# ifndef HAVE_GETHOSTBYNAME2
-
-/**/
-static struct hostent *
-zsh_gethostbyname2(char const *name, int af)
-{
-	if(af != AF_INET) {
-		h_errno = NO_RECOVERY;
-		return NULL;
-	}
-	return gethostbyname(name);
-}
-
-#else /* !HAVE_GETHOSTBYNAME2 */
-
-# define zsh_gethostbyname2 gethostbyname2
-
-/**/
-# endif /* !HAVE_GETHOSTBYNAME2 */
-
-
-/* note: this is not a complete implementation.  If ignores the flags,
-   and does not provide the memory allocation of the standard interface.
-   Each returned structure will overwrite the previous one. */
-
-/**/
-static 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 };
-# ifdef SUPPORT_IPV6
-	static char pbuf[INET6_ADDRSTRLEN];
-# else
-	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;
-}
-/**/
-static void
-freehostent(struct hostent *ptr)
-{
-}
-
-#else /* !HAVE_GETIPNODEBYNAME */
-
-# define zsh_getipnodebyname getipnodebyname
-
-/**/
-#endif /* !HAVE_GETIPNODEBYNAME */
-
 /*
  * For FTP block mode
  *
@@ -476,10 +327,8 @@ struct zftp_session {
     char *name;			/* name of session */
     char **params;		/* parameters ordered as in zfparams */
     char **userparams;		/* user parameters set by zftp_params */
-    int cfd;			/* control file descriptor */
     FILE *cin;			/* control input file */
-    union zftp_sockaddr sock;	/* this end of the control connection */
-    union zftp_sockaddr peer;	/* far end of the control connection */
+    struct tcp_session control;	/* the control connection */
     int dfd;			/* data connection */
     int has_size;		/* understands SIZE? */
     int has_mdtm;		/* understands MDTM? */
@@ -821,7 +670,7 @@ zfgetline(char *ln, int lnsize, int tmout)
 		cmdbuf[0] = (char)IAC;
 		cmdbuf[1] = (char)DONT;
 		cmdbuf[2] = ch;
-		write(zfsess->cfd, cmdbuf, 3);
+		write(zfsess->control.fd, cmdbuf, 3);
 		continue;
 
 	    case DO:
@@ -831,7 +680,7 @@ zfgetline(char *ln, int lnsize, int tmout)
 		cmdbuf[0] = (char)IAC;
 		cmdbuf[1] = (char)WONT;
 		cmdbuf[2] = ch;
-		write(zfsess->cfd, cmdbuf, 3);
+		write(zfsess->control.fd, cmdbuf, 3);
 		continue;
 
 	    case EOF:
@@ -881,7 +730,7 @@ zfgetmsg(void)
     char line[256], *ptr, *verbose;
     int stopit, printing = 0, tmout;
 
-    if (zfsess->cfd == -1)
+    if (zfsess->control.fd == -1)
 	return 6;
     zsfree(lastmsg);
     lastmsg = NULL;
@@ -1009,7 +858,7 @@ zfsendcmd(char *cmd)
      */
     int ret, tmout;
 
-    if (zfsess->cfd == -1)
+    if (zfsess->control.fd == -1)
 	return 6;
     tmout = getiparam("ZFTP_TMOUT");
     if (setjmp(zfalrmbuf)) {
@@ -1018,7 +867,7 @@ zfsendcmd(char *cmd)
 	return 6;
     }
     zfalarm(tmout);
-    ret = write(zfsess->cfd, cmd, strlen(cmd));
+    ret = write(zfsess->control.fd, cmd, strlen(cmd));
     alarm(0);
 
     if (ret <= 0) {
@@ -1035,7 +884,7 @@ zfsendcmd(char *cmd)
 
 /**/
 static int
-zfopendata(char *name, union zftp_sockaddr *zdsockp, int *is_passivep)
+zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
 {
     if (!(zfprefs & (ZFPF_SNDP|ZFPF_PASV))) {
 	zwarnnam(name, "Must set preference S or P to transfer data", NULL, 0);
@@ -1052,7 +901,7 @@ zfopendata(char *name, union zftp_sockaddr *zdsockp, int *is_passivep)
 	int err, salen;
 
 #ifdef SUPPORT_IPV6
-	if(zfsess->peer.a.sa_family == AF_INET6)
+	if(zfsess->control.peer.a.sa_family == AF_INET6)
 	    psv_cmd = "EPSV\r\n";
 	else
 #endif /* SUPPORT_IPV6 */
@@ -1068,9 +917,9 @@ zfopendata(char *name, union zftp_sockaddr *zdsockp, int *is_passivep)
 	    zfclosedata();
 	    return zfopendata(name, zdsockp, is_passivep);
 	}
-	zdsockp->a.sa_family = zfsess->peer.a.sa_family;
+	zdsockp->a.sa_family = zfsess->control.peer.a.sa_family;
 #ifdef SUPPORT_IPV6
-	if(zfsess->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;
@@ -1098,7 +947,7 @@ zfopendata(char *name, union zftp_sockaddr *zdsockp, int *is_passivep)
 	    portnum = strtoul(portbuf, &pbp, 10);
 	    if(*pbp || portnum > 65535UL)
 		goto bad_epsv;
-	    *zdsockp = zfsess->peer;
+	    *zdsockp = zfsess->control.peer;
 	    zdsockp->in6.sin6_port = htons((unsigned)portnum);
 	    salen = sizeof(struct sockaddr_in6);
 	} else
@@ -1157,7 +1006,7 @@ zfopendata(char *name, union zftp_sockaddr *zdsockp, int *is_passivep)
 	    return 1;
 	}
 
-	*zdsockp = zfsess->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() */
@@ -1243,7 +1092,7 @@ static int
 zfgetdata(char *name, char *rest, char *cmd, int getsize)
 {
     int len, newfd, is_passive;
-    union zftp_sockaddr zdsock;
+    union tcp_sockaddr zdsock;
 
     if (zfopendata(name, &zdsock, &is_passive))
 	return 1;
@@ -1831,8 +1680,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->cfd, (char *)msg, 3, 0);
-	send(zfsess->cfd, (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) {
@@ -1893,7 +1742,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->cfd != -1)
+    if (zfsess->control.fd != -1)
 	zfclose(0);
 
     /* this is going to give 0.  why bother? */
@@ -1950,24 +1799,24 @@ zftp_open(char *name, char **args, int flags)
 	}
 	zfsetparam("ZFTP_HOST", ztrdup(zhostp->h_name), ZFPM_READONLY);
 
-	zfsess->peer.a.sa_family = af;
+	zfsess->control.peer.a.sa_family = af;
 #ifdef SUPPORT_IPV6
 	if(af == AF_INET6) {
-	    zfsess->peer.in6.sin6_port = zservp->s_port;
-	    zfsess->peer.in6.sin6_flowinfo = 0;
+	    zfsess->control.peer.in6.sin6_port = zservp->s_port;
+	    zfsess->control.peer.in6.sin6_flowinfo = 0;
 # ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
-	    zfsess->peer.in6.sin6_scope_id = 0;
+	    zfsess->control.peer.in6.sin6_scope_id = 0;
 # endif
 	    salen = sizeof(struct sockaddr_in6);
 	} else
 #endif /* SUPPORT_IPV6 */
 	{
-	    zfsess->peer.in.sin_port = zservp->s_port;
+	    zfsess->control.peer.in.sin_port = zservp->s_port;
 	    salen = sizeof(struct sockaddr_in);
 	}
 
-	zfsess->cfd = socket(af, SOCK_STREAM, 0);
-	if (zfsess->cfd < 0) {
+	tcp_socket(af, SOCK_STREAM, 0, &(zfsess->control));
+	if (zfsess->control.fd < 0) {
 	    freehostent(zhostp);
 	    zfunsetparam("ZFTP_HOST");
 	    FAILED();
@@ -1989,12 +1838,12 @@ zftp_open(char *name, char **args, int flags)
 	for (addrp = zhostp->h_addr_list; err && *addrp; addrp++) {
 #ifdef SUPPORT_IPV6
 	    if(af == AF_INET6)
-		memcpy(&zfsess->peer.in6.sin6_addr, *addrp, zhostp->h_length);
+		memcpy(&zfsess->control.peer.in6.sin6_addr, *addrp, zhostp->h_length);
 	    else
 #endif /* SUPPORT_IPV6 */
-		memcpy(&zfsess->peer.in.sin_addr, *addrp, zhostp->h_length);
+		memcpy(&zfsess->control.peer.in.sin_addr, *addrp, zhostp->h_length);
 	    do {
-		err = connect(zfsess->cfd, (struct sockaddr *)&zfsess->peer,
+		err = connect(zfsess->control.fd, (struct sockaddr *)&zfsess->control.peer,
 			      salen);
 	    } while (err && errno == EINTR && !errflag);
 	    /* you can check whether it's worth retrying here */
@@ -2030,15 +1879,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->cfd = zfmovefd(zfsess->cfd);
+    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->cfd, F_SETFD, FD_CLOEXEC);
+    fcntl(zfsess->control.fd, F_SETFD, FD_CLOEXEC);
 #endif
 
-    len = sizeof(zfsess->sock);
-    if (getsockname(zfsess->cfd, (struct sockaddr *)&zfsess->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;
@@ -2050,20 +1899,20 @@ zftp_open(char *name, char **args, int flags)
      * do clever things with SIGURG.
      */
     len = 1;
-    setsockopt(zfsess->cfd, 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->cfd, 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->cfd, "r");
+    zfsess->cin = fdopen(zfsess->control.fd, "r");
 
     if (!zfsess->cin) {
 	zwarnnam(name, "file handling error", NULL, 0);
@@ -2107,17 +1956,17 @@ zftp_open(char *name, char **args, int flags)
 	unlink(fname);
     }
 
-    if (zfsess->cfd == -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->cfd > -1 && *++args)
+    if (zfsess->control.fd > -1 && *++args)
 	return zftp_login(name, args, flags);
     /* if something wayward happened, connection was already closed */
-    return zfsess->cfd == -1;
+    return zfsess->control.fd == -1;
 }
 
 /*
@@ -2311,7 +2160,7 @@ zftp_login(char *name, char **args, int flags)
     }
 
     zsfree(ucmd);
-    if (zfsess->cfd == -1)
+    if (zfsess->control.fd == -1)
 	return 1;
     if (stopit == 2 || (lastcode != 230 && lastcode != 202)) {
 	zwarnnam(name, "login failed", NULL, 0);
@@ -2390,7 +2239,7 @@ zftp_test(char *name, char **args, int flags)
     struct timeval tv;
 # endif /* HAVE_POLL */
 
-    if (zfsess->cfd == -1)
+    if (zfsess->control.fd == -1)
 	return 1;
 
 # ifdef HAVE_POLL
@@ -2398,7 +2247,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->cfd;
+    pfd.fd = zfsess->control.fd;
     pfd.events = POLLIN;
     if ((ret = poll(&pfd, 1, 0)) < 0 && errno != EINTR && errno != EAGAIN)
 	zfclose(0);
@@ -2408,10 +2257,10 @@ zftp_test(char *name, char **args, int flags)
     }
 # else
     FD_ZERO(&f);
-    FD_SET(zfsess->cfd, &f);
+    FD_SET(zfsess->control.fd, &f);
     tv.tv_sec = 0;
     tv.tv_usec = 0;
-    if ((ret = select(zfsess->cfd +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);
@@ -2420,8 +2269,8 @@ zftp_test(char *name, char **args, int flags)
 	zfgetmsg();
     }
 # endif /* HAVE_POLL */
-    /* if we now have zfsess->cfd == -1, then we've just been dumped out. */
-    return (zfsess->cfd == -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;
@@ -2843,7 +2692,7 @@ zfclose(int leaveparams)
     char **aptr;
     Eprog prog;
 
-    if (zfsess->cfd == -1)
+    if (zfsess->control.fd == -1)
 	return;
 
     zfclosing = 1;
@@ -2859,10 +2708,10 @@ zfclose(int leaveparams)
 	fclose(zfsess->cin);
 	zfsess->cin = NULL;
     }
-    if (zfsess->cfd != -1) {
+    if (zfsess->control.fd != -1) {
 	zfnopen--;
-	close(zfsess->cfd);
-	zfsess->cfd = -1;
+	close(zfsess->control.fd);
+	zfsess->control.fd = -1;
     }
 
     if (zfstatfd != -1) {
@@ -2935,7 +2784,7 @@ newsession(char *nm)
     if (!nptr) {
 	zfsess = (Zftp_session) zcalloc(sizeof(struct zftp_session));
 	zfsess->name = ztrdup(nm);
-	zfsess->cfd = zfsess->dfd = -1;
+	zfsess->control.fd = zfsess->dfd = -1;
 	zfsess->params = (char **) zcalloc(sizeof(zfparams));
 	zaddlinknode(zfsessions, zfsess);
 
@@ -3150,7 +2999,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->cfd != -1 && (zfstatusp[zfsessno] & ZFST_CLOS)) {
+	if (zfsess->control.fd != -1 && (zfstatusp[zfsessno] & ZFST_CLOS)) {
 	    /* got closed in subshell without us knowing */
 	    zcfinish = 2;
 	    zfclose(0);
@@ -3171,7 +3020,7 @@ bin_zftp(char *name, char **args, char *ops, int func)
 	}
     }
 #if defined(HAVE_SELECT) || defined (HAVE_POLL)
-    if (zfsess->cfd != -1 && !(zptr->flags & (ZFTP_TEST|ZFTP_SESS))) {
+    if (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
@@ -3181,7 +3030,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->cfd == -1) {
+    if ((zptr->flags & ZFTP_CONN) && zfsess->control.fd == -1) {
 	if (ret != 2) {
 	    /*
 	     * with ret == 2, we just got dumped out in the test,
diff --git a/Src/Modules/zftp.mdd b/Src/Modules/zftp.mdd
index d9619b549..6886fd958 100644
--- a/Src/Modules/zftp.mdd
+++ b/Src/Modules/zftp.mdd
@@ -5,4 +5,6 @@ functions='Functions/Zftp/*'
 
 autobins="zftp"
 
+moddeps="zsh/tcp"
+
 objects="zftp.o"