summaryrefslogtreecommitdiff
path: root/Src/Modules/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Modules/tcp.c')
-rw-r--r--Src/Modules/tcp.c308
1 files changed, 260 insertions, 48 deletions
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)