diff -r 000000000000 -r 2e3d3ce01487 openenvutils/commandshell/shell/src/modules/tcp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openenvutils/commandshell/shell/src/modules/tcp.c Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,732 @@ +// tcp.c - TCP module +// +// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. +// +/* + * 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" + +#ifdef __SYMBIAN32__ +#ifdef __WINSCW__ +#pragma warn_unusedarg off +#endif//__WINSCW__ +#endif//__SYMBIAN32__ + +#ifdef __SYMBIAN32__ +#include "dummy.h" +#endif //__SYMBIAN32__ +/* + * 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 +#ifndef __SYMBIAN32__ +# include +#else +# include "poll.h" +#endif +#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 = (struct hostent *)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 + +#ifndef __SYMBIAN32__ + 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; + } + +#endif//__SYMBIAN32__ + + 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)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; +}