about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPaul Ackersviller <packersv@users.sourceforge.net>2007-11-11 22:42:17 +0000
committerPaul Ackersviller <packersv@users.sourceforge.net>2007-11-11 22:42:17 +0000
commit41de8626a75d8151f91b439a6223d7b9883bdf89 (patch)
tree0b02b34631c55db0c2e38fc898cde63cebd1cd61 /Src
parentd9fa8166889119d1b827c0326dbaf4e90e7b2b30 (diff)
downloadzsh-41de8626a75d8151f91b439a6223d7b9883bdf89.tar.gz
zsh-41de8626a75d8151f91b439a6223d7b9883bdf89.tar.xz
zsh-41de8626a75d8151f91b439a6223d7b9883bdf89.zip
Merge of 23168: error when failing to bind to a port didn't use native byte order.
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/tcp.c712
1 files changed, 712 insertions, 0 deletions
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
new file mode 100644
index 000000000..8fde5d04e
--- /dev/null
+++ b/Src/Modules/tcp.c
@@ -0,0 +1,712 @@
+/*
+ * tcp.c - TCP module
+ *
+ * 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"
+#include "tcp.mdh"
+
+/*
+ * 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_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 */
+
+/**/
+#ifndef HAVE_INET_PTON
+
+/**/
+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, UNUSED(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(UNUSED(struct hostent *ptr))
+{
+}
+
+/**/
+#else /* !HAVE_GETIPNODEBYNAME */
+
+/**/
+# define zsh_getipnodebyname getipnodebyname
+
+/**/
+#endif /* !HAVE_GETIPNODEBYNAME */
+
+LinkList ztcp_sessions;
+
+/* "allocate" a tcp_session */
+static Tcp_session
+zts_alloc(int ztflags)
+{
+    Tcp_session sess;
+
+    sess = (Tcp_session)zshcalloc(sizeof(struct tcp_session));
+    if (!sess) return NULL;
+    sess->fd=-1;
+    sess->flags=ztflags;
+
+    zinsertlinknode(ztcp_sessions, lastnode(ztcp_sessions), (void *)sess);
+
+    return 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;
+}
+
+static int
+ztcp_free_session(Tcp_session sess)
+{
+    zfree(sess, sizeof(struct tcp_session));
+
+    return 0;
+}
+
+static int
+zts_delete(Tcp_session sess)
+{
+    LinkNode node;
+
+    node = linknodebydatum(ztcp_sessions, (void *)sess);
+
+    if (!node)
+    {
+	return 1;
+    }
+
+    ztcp_free_session(getdata(node));
+    remnode(ztcp_sessions, node);
+
+    return 0;
+}
+
+static Tcp_session
+zts_byfd(int fd)
+{
+    LinkNode node;
+    
+    for (node = firstnode(ztcp_sessions); node; incnode(node))
+	if (((Tcp_session)getdata(node))->fd == fd)
+	    return (Tcp_session)getdata(node);
+    
+    return NULL;
+}
+
+static void
+tcp_cleanup(void)
+{
+    LinkNode node, next;
+
+    for (node = firstnode(ztcp_sessions); node; node = next) {
+	next = node->next;
+	tcp_close((Tcp_session)getdata(node));
+    }
+}
+
+/**/
+mod_export int
+tcp_close(Tcp_session sess)
+{
+    int err;
+    
+    if (sess)
+    {  
+	if (sess->fd != -1)
+	{
+	    err = close(sess->fd);
+	    if (err)
+		zwarn("connection close failed: %e", NULL, errno);
+	}
+	zts_delete(sess);
+	return 0;
+    }
+
+    return -1;
+}
+
+/**/
+mod_export int
+tcp_connect(Tcp_session sess, char *addrp, struct hostent *zhost, int d_port)
+{
+    int salen;
+#ifdef SUPPORT_IPV6
+    if (zhost->h_addrtype==AF_INET6) {
+	memcpy(&(sess->peer.in6.sin6_addr), addrp, zhost->h_length);
+	sess->peer.in6.sin6_port = d_port;
+	sess->peer.in6.sin6_flowinfo = 0;
+# ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
+	sess->peer.in6.sin6_scope_id = 0;
+# endif
+	sess->peer.in6.sin6_family = zhost->h_addrtype;
+	salen = sizeof(struct sockaddr_in6);
+    } else
+#endif /* SUPPORT_IPV6 */
+    {
+	memcpy(&(sess->peer.in.sin_addr), addrp, zhost->h_length);
+	sess->peer.in.sin_port = d_port;
+	sess->peer.in.sin_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, Options ops, UNUSED(int func))
+{
+    int herrno, err=1, destport, force=0, verbose=0, test=0, targetfd=0;
+    ZSOCKLEN_T  len;
+    char **addrp, *desthost, *localname, *remotename;
+    struct hostent *zthost = NULL, *ztpeer = NULL;
+    struct servent *srv;
+    Tcp_session sess = NULL;
+
+    if (OPT_ISSET(ops,'f'))
+	force = 1;
+
+    if (OPT_ISSET(ops,'v'))
+	verbose = 1;
+
+    if (OPT_ISSET(ops,'t'))
+        test = 1;
+
+    if (OPT_ISSET(ops,'d')) {
+	targetfd = atoi(OPT_ARG(ops,'d'));
+	if (!targetfd) {
+	    zwarnnam(nam, "%s is an invalid argument to -d",
+		     OPT_ARG(ops,'d'), 0);
+	    return 1;
+	}
+    }
+
+
+    if (OPT_ISSET(ops,'c')) {
+	if (!args[0]) {
+	    tcp_cleanup();
+	}
+	else {
+	    targetfd = atoi(args[0]);
+	    sess = zts_byfd(targetfd);
+	    if(!targetfd) {
+		zwarnnam(nam, "%s is an invalid argument to -c", args[0], 0);
+		return 1;
+	    }
+
+	    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);
+		return 0;
+	    }
+	    else
+	    {
+		zwarnnam(nam, "fd %s not found in tcp table", args[0], 0);
+		return 1;
+	    }
+	}
+    }
+    else if (OPT_ISSET(ops,'l')) {
+	int lport = 0;
+
+	if (!args[0]) {
+	    zwarnnam(nam, "-l requires an argument", NULL, 0);
+	    return 1;
+	}
+
+	srv = getservbyname(args[0], "tcp");
+	if (srv)
+	    lport = srv->s_port;
+	else
+	    lport = htons(atoi(args[0]));
+	if (!lport) { zwarnnam(nam, "bad service name or port number", NULL, 0);
+	return 1;
+	}
+	sess = tcp_socket(PF_INET, SOCK_STREAM, 0, ZTCP_LISTEN);
+
+	if (!sess) {
+	    zwarnnam(nam, "unable to allocate a TCP session slot", NULL, 0);
+	    return 1;
+	}
+#ifdef SO_OOBINLINE
+	len = 1;
+	setsockopt(sess->fd, SOL_SOCKET, SO_OOBINLINE, (char *)&len, sizeof(len));
+#endif
+	if (!zsh_inet_aton("0.0.0.0", &(sess->sock.in.sin_addr)))
+	{
+	    zwarnnam(nam, "bad address: %s", "0.0.0.0", 0);
+	    return 1;
+	}
+
+	sess->sock.in.sin_family = AF_INET;
+	sess->sock.in.sin_port = lport;
+
+
+	if (bind(sess->fd, (struct sockaddr *)&sess->sock.in, sizeof(struct sockaddr_in)))
+	{
+	    char buf[DIGBUFSIZE];
+	    convbase(buf, (zlong)ntohs(lport), 10);
+	    zwarnnam(nam, "could not bind to port %s: %e", buf, errno);
+	    tcp_close(sess);
+	    return 1;
+	}
+
+	if (listen(sess->fd, 1))
+	{
+	    zwarnnam(nam, "could not listen on socket: %e", NULL, errno);
+	    tcp_close(sess);
+	    return 1;
+	}
+
+	if (targetfd) {
+	    redup(sess->fd,targetfd);
+	    sess->fd = targetfd;
+	}
+	else {
+	    /* move the fd since no one will want to read from it */
+	    sess->fd = movefd(sess->fd);
+	}
+
+	setiparam("REPLY", sess->fd);
+
+	if (verbose)
+	    printf("%d listener is on fd %d\n", ntohs(sess->sock.in.sin_port), sess->fd);
+
+	return 0;
+
+    }
+    else if (OPT_ISSET(ops,'a'))
+    {
+	int lfd, rfd;
+
+	if (!args[0]) {
+	    zwarnnam(nam, "-a requires an argument", NULL, 0);
+	    return 1;
+	}
+
+	lfd = atoi(args[0]);
+
+	if (!lfd) {
+	    zwarnnam(nam, "invalid numerical argument", NULL, 0);
+	    return 1;
+	}
+
+	sess = zts_byfd(lfd);
+	if (!sess) {
+	    zwarnnam(nam, "fd %s is not registered as a tcp connection", args[0], 0);
+	    return 1;
+	}
+
+	if (!(sess->flags & ZTCP_LISTEN))
+	{
+	    zwarnnam(nam, "tcp connection not a listener", NULL, 0);
+	    return 1;
+	}
+
+	if (test) {
+#if defined(HAVE_POLL) || defined(HAVE_SELECT)
+# ifdef HAVE_POLL
+	    struct pollfd pfd;
+	    int ret;
+
+	    pfd.fd = lfd;
+	    pfd.events = POLLIN;
+	    if ((ret = poll(&pfd, 1, 0)) == 0) return 1;
+	    else if (ret == -1)
+	    {
+		zwarnnam(nam, "poll error: %e", NULL, errno);
+		return 1;
+	    }
+# else
+	    fd_set rfds;
+	    struct timeval tv;
+	    int ret;
+	    
+	    FD_ZERO(&rfds);
+	    FD_SET(lfd, &rfds);
+	    tv.tv_sec = 0;
+	    tv.tv_usec = 0;
+	    
+	    if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1;
+	    else if (ret == -1)
+	    {
+		zwarnnam(nam, "select error: %e", NULL, errno);
+		return 1;
+	    }
+	    
+# endif
+	    
+#else
+	    zwarnnam(nam, "not currently supported", NULL, 0);
+	    return 1;
+#endif
+	}
+	sess = zts_alloc(ZTCP_INBOUND);
+
+	len = sizeof(sess->peer.in);
+	if ((rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len)) == -1)
+	{
+	    zwarnnam(nam, "could not accept connection: %e", NULL, errno);
+	    tcp_close(sess);
+	    return 1;
+	}
+
+	if (targetfd) {
+	    redup(rfd, targetfd);
+	    sess->fd = targetfd;
+	}
+	else {
+	    sess->fd = rfd;
+	}
+
+	setiparam("REPLY", sess->fd);
+
+	if (verbose)
+	    printf("%d is on fd %d\n", ntohs(sess->peer.in.sin_port), sess->fd);
+    }
+    else
+    {
+	if (!args[0]) {
+	    LinkNode node;
+	    for(node = firstnode(ztcp_sessions); node; incnode(node))
+	    {
+		sess = (Tcp_session)getdata(node);
+
+		if (sess->fd != -1)
+		{
+		    zthost = gethostbyaddr((const void *)&(sess->sock.in.sin_addr), sizeof(sess->sock.in.sin_addr), AF_INET);
+		    if (zthost)
+			localname = zthost->h_name;
+		    else
+			localname = ztrdup(inet_ntoa(sess->sock.in.sin_addr));
+		    ztpeer = gethostbyaddr((const void *)&(sess->peer.in.sin_addr), sizeof(sess->peer.in.sin_addr), AF_INET);
+		    if (ztpeer)
+			remotename = ztpeer->h_name;
+		    else
+			remotename = ztrdup(inet_ntoa(sess->peer.in.sin_addr));
+		    if (OPT_ISSET(ops,'L')) {
+			int schar;
+			if (sess->flags & ZTCP_ZFTP)
+			    schar = 'Z';
+			else if (sess->flags & ZTCP_LISTEN)
+			    schar = 'L';
+			else if (sess->flags & ZTCP_INBOUND)
+			    schar = 'I';
+			else
+			    schar = 'O';
+			printf("%d %c %s %d %s %d\n",
+			       sess->fd, schar,
+			       localname, ntohs(sess->sock.in.sin_port),
+			       remotename, ntohs(sess->peer.in.sin_port));
+		    } else {
+			printf("%s:%d %s %s:%d is on fd %d%s\n",
+			       localname, ntohs(sess->sock.in.sin_port),
+			       ((sess->flags & ZTCP_LISTEN) ? "-<" :
+				((sess->flags & ZTCP_INBOUND) ? "<-" : "->")),
+			       remotename, ntohs(sess->peer.in.sin_port),
+			       sess->fd,
+			       (sess->flags & ZTCP_ZFTP) ? " ZFTP" : "");
+		    }
+		}
+	    }
+	    return 0;
+	}
+	else if (!args[1]) {
+	    destport = htons(23);
+	}
+	else {
+
+	    srv = getservbyname(args[1],"tcp");
+	    if (srv)
+		destport = srv->s_port;
+	    else
+		destport = htons(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);
+
+	if (!sess) {
+	    zwarnnam(nam, "unable to allocate a TCP session slot", NULL, 0);
+	    return 1;
+	}
+
+#ifdef SO_OOBINLINE
+	len = 1;
+	setsockopt(sess->fd, SOL_SOCKET, SO_OOBINLINE, (char *)&len, sizeof(len));
+#endif
+
+	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, destport);
+	    } while (err && errno == EINTR && !errflag);
+	}
+	
+	if (err) {
+	    zwarnnam(nam, "connection failed: %e", NULL, errno);
+	    tcp_close(sess);
+	    zsfree(desthost);
+	    return 1;
+	}
+	else
+	{
+	    if (targetfd) {
+		redup(sess->fd, targetfd);
+		sess->fd = targetfd;
+	    }
+
+	    setiparam("REPLY", sess->fd);
+
+	    if (verbose)
+		printf("%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, 3, 0, "acd:flLtv", NULL),
+};
+
+/* The load/unload routines required by the zsh library interface */
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+    return 0;
+}
+
+/**/
+int
+boot_(Module m)
+{
+    ztcp_sessions = znewlinklist();
+    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+}
+
+
+/**/
+int
+cleanup_(Module m)
+{
+    tcp_cleanup();
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    freelinklist(ztcp_sessions, (FreeFunc) ztcp_free_session);
+    return 0;
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+    return 0;
+}