mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			733 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			733 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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", 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'));
 | |
| 	    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]);
 | |
| 		return 1;
 | |
| 	    }
 | |
| 
 | |
| 	    if (sess)
 | |
| 	    {
 | |
| 		if ((sess->flags & ZTCP_ZFTP) && !force)
 | |
| 		{
 | |
| 		    zwarnnam(nam, "use -f to force closure of a zftp control connection");
 | |
| 		    return 1;
 | |
| 		}
 | |
| 		tcp_close(sess);
 | |
| 		return 0;
 | |
| 	    }
 | |
| 	    else
 | |
| 	    {
 | |
| 		zwarnnam(nam, "fd %s not found in tcp table", args[0]);
 | |
| 		return 1;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     else if (OPT_ISSET(ops,'l')) {
 | |
| 	int lport = 0;
 | |
| 
 | |
| 	if (!args[0]) {
 | |
| 	    zwarnnam(nam, "-l requires an argument");
 | |
| 	    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");
 | |
| 	return 1;
 | |
| 	}
 | |
| 	sess = tcp_socket(PF_INET, SOCK_STREAM, 0, ZTCP_LISTEN);
 | |
| 
 | |
| 	if (!sess) {
 | |
| 	    zwarnnam(nam, "unable to allocate a TCP session slot");
 | |
| 	    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");
 | |
| 	    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", 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");
 | |
| 	    return 1;
 | |
| 	}
 | |
| 
 | |
| 	lfd = atoi(args[0]);
 | |
| 
 | |
| 	if (!lfd) {
 | |
| 	    zwarnnam(nam, "invalid numerical argument");
 | |
| 	    return 1;
 | |
| 	}
 | |
| 
 | |
| 	sess = zts_byfd(lfd);
 | |
| 	if (!sess) {
 | |
| 	    zwarnnam(nam, "fd %s is not registered as a tcp connection", args[0]);
 | |
| 	    return 1;
 | |
| 	}
 | |
| 
 | |
| 	if (!(sess->flags & ZTCP_LISTEN))
 | |
| 	{
 | |
| 	    zwarnnam(nam, "tcp connection not a listener");
 | |
| 	    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", 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", errno);
 | |
| 		return 1;
 | |
| 	    }
 | |
| 	    
 | |
| # endif
 | |
| 	    
 | |
| #else
 | |
| 	    zwarnnam(nam, "not currently supported");
 | |
| 	    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", 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);
 | |
| 	    return 1;
 | |
| 	}
 | |
| 	
 | |
| 	sess = tcp_socket(PF_INET, SOCK_STREAM, 0, 0);
 | |
| 
 | |
| 	if (!sess) {
 | |
| 	    zwarnnam(nam, "unable to allocate a TCP session slot");
 | |
| 	    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", 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");
 | |
| 	    do {
 | |
| 		err = tcp_connect(sess, *addrp, zthost, destport);
 | |
| 	    } while (err && errno == EINTR && !errflag);
 | |
| 	}
 | |
| 	
 | |
| 	if (err) {
 | |
| 	    zwarnnam(nam, "connection failed: %e", 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),
 | |
| };
 | |
| 
 | |
| static struct features module_features = {
 | |
|     bintab, sizeof(bintab)/sizeof(*bintab),
 | |
|     NULL, 0,
 | |
|     NULL, 0,
 | |
|     NULL, 0,
 | |
|     0
 | |
| };
 | |
| 
 | |
| /* The load/unload routines required by the zsh library interface */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| setup_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| features_(Module m, char ***features)
 | |
| {
 | |
|     *features = featuresarray(m, &module_features);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| enables_(Module m, int **enables)
 | |
| {
 | |
|     return handlefeatures(m, &module_features, enables);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| boot_(Module m)
 | |
| {
 | |
|     ztcp_sessions = znewlinklist();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**/
 | |
| int
 | |
| cleanup_(Module m)
 | |
| {
 | |
|     tcp_cleanup();
 | |
|     freelinklist(ztcp_sessions, (FreeFunc) ztcp_free_session);
 | |
|     return setfeatureenables(m, &module_features, NULL);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| finish_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 |