1/*
2 * tcp.c - TCP module
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1998-2001 Peter Stephenson
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Peter Stephenson or the Zsh Development
16 * Group be liable to any party for direct, indirect, special, incidental,
17 * or consequential damages arising out of the use of this software and
18 * its documentation, even if Peter Stephenson, and the Zsh
19 * Development Group have been advised of the possibility of such damage.
20 *
21 * Peter Stephenson and the Zsh Development Group specifically
22 * disclaim any warranties, including, but not limited to, the implied
23 * warranties of merchantability and fitness for a particular purpose.  The
24 * software provided hereunder is on an "as is" basis, and Peter Stephenson
25 * and the Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30/*
31 * We need to include the zsh headers later to avoid clashes with
32 * the definitions on some systems, however we need the configuration
33 * file to decide whether we can include netinet/in_systm.h, which
34 * doesn't exist on cygwin.
35 */
36#include "tcp.h"
37#include "tcp.mdh"
38
39/*
40 * We use poll() in preference to select because some subset of manuals says
41 * that's the thing to do, plus it's a bit less fiddly.  I don't actually
42 * have access to a system with poll but not select, however, though
43 * both bits of the code have been tested on a machine with both.
44 */
45#ifdef HAVE_POLL_H
46# include <poll.h>
47#endif
48#if defined(HAVE_POLL) && !defined(POLLIN) && !defined(POLLNORM)
49# undef HAVE_POLL
50#endif
51
52#ifdef USE_LOCAL_H_ERRNO
53int h_errno;
54#endif
55
56/* We use the RFC 2553 interfaces.  If the functions don't exist in the
57 * library, simulate them. */
58
59#ifndef INET_ADDRSTRLEN
60# define INET_ADDRSTRLEN 16
61#endif
62
63#ifndef INET6_ADDRSTRLEN
64# define INET6_ADDRSTRLEN 46
65#endif
66
67/**/
68#ifndef HAVE_INET_NTOP
69
70/**/
71mod_export char const *
72zsh_inet_ntop(int af, void const *cp, char *buf, size_t len)
73{
74    if (af != AF_INET) {
75	errno = EAFNOSUPPORT;
76	return NULL;
77    }
78    if (len < INET_ADDRSTRLEN) {
79	errno = ENOSPC;
80	return NULL;
81    }
82    strcpy(buf, inet_ntoa(*(struct in_addr *)cp));
83    return buf;
84}
85
86/**/
87#else /* !HAVE_INET_NTOP */
88
89/**/
90# define zsh_inet_ntop inet_ntop
91
92/**/
93#endif /* !HAVE_INET_NTOP */
94
95/**/
96#ifndef HAVE_INET_ATON
97
98# ifndef INADDR_NONE
99#  define INADDR_NONE 0xffffffffUL
100# endif
101
102/**/
103mod_export int zsh_inet_aton(char const *src, struct in_addr *dst)
104{
105    return (dst->s_addr = inet_addr(src)) != INADDR_NONE;
106}
107
108/**/
109#else /* !HAVE_INET_ATON */
110
111/**/
112# define zsh_inet_aton inet_aton
113
114/**/
115#endif /* !HAVE_INET_ATON */
116
117/**/
118#ifndef HAVE_INET_PTON
119
120/**/
121mod_export int
122zsh_inet_pton(int af, char const *src, void *dst)
123{
124    if (af != AF_INET) {
125	errno = EAFNOSUPPORT;
126	return -1;
127    }
128    return !!zsh_inet_aton(src, dst);
129}
130
131#else /* !HAVE_INET_PTON */
132
133# define zsh_inet_pton inet_pton
134
135/**/
136#endif /* !HAVE_INET_PTON */
137
138/**/
139#ifndef HAVE_GETIPNODEBYNAME
140
141/**/
142# ifndef HAVE_GETHOSTBYNAME2
143
144/**/
145mod_export struct hostent *
146zsh_gethostbyname2(char const *name, int af)
147{
148    if (af != AF_INET) {
149	h_errno = NO_RECOVERY;
150	return NULL;
151    }
152    return gethostbyname(name);
153}
154
155/**/
156#else /* !HAVE_GETHOSTBYNAME2 */
157
158/**/
159# define zsh_gethostbyname2 gethostbyname2
160
161/**/
162# endif /* !HAVE_GETHOSTBYNAME2 */
163
164/* note: this is not a complete implementation.  If ignores the flags,
165   and does not provide the memory allocation of the standard interface.
166   Each returned structure will overwrite the previous one. */
167
168/**/
169mod_export struct hostent *
170zsh_getipnodebyname(char const *name, int af, UNUSED(int flags), int *errorp)
171{
172    static struct hostent ahe;
173    static char nbuf[16];
174    static char *addrlist[] = { nbuf, NULL };
175# ifdef SUPPORT_IPV6
176    static char pbuf[INET6_ADDRSTRLEN];
177# else
178    static char pbuf[INET_ADDRSTRLEN];
179# endif
180    struct hostent *he;
181    if (zsh_inet_pton(af, name, nbuf) == 1) {
182	zsh_inet_ntop(af, nbuf, pbuf, sizeof(pbuf));
183	ahe.h_name = pbuf;
184	ahe.h_aliases = addrlist+1;
185	ahe.h_addrtype = af;
186	ahe.h_length = (af == AF_INET) ? 4 : 16;
187	ahe.h_addr_list = addrlist;
188	return &ahe;
189    }
190    he = zsh_gethostbyname2(name, af);
191    if (!he)
192	*errorp = h_errno;
193    return he;
194}
195
196/**/
197mod_export void
198freehostent(UNUSED(struct hostent *ptr))
199{
200}
201
202/**/
203#else /* !HAVE_GETIPNODEBYNAME */
204
205/**/
206# define zsh_getipnodebyname getipnodebyname
207
208/**/
209#endif /* !HAVE_GETIPNODEBYNAME */
210
211LinkList ztcp_sessions;
212
213/* "allocate" a tcp_session */
214static Tcp_session
215zts_alloc(int ztflags)
216{
217    Tcp_session sess;
218
219    sess = (Tcp_session)zshcalloc(sizeof(struct tcp_session));
220    if (!sess) return NULL;
221    sess->fd=-1;
222    sess->flags=ztflags;
223
224    zinsertlinknode(ztcp_sessions, lastnode(ztcp_sessions), (void *)sess);
225
226    return sess;
227}
228
229/**/
230mod_export Tcp_session
231tcp_socket(int domain, int type, int protocol, int ztflags)
232{
233    Tcp_session sess;
234
235    sess = zts_alloc(ztflags);
236    if (!sess) return NULL;
237
238    sess->fd = socket(domain, type, protocol);
239    return sess;
240}
241
242static int
243ztcp_free_session(Tcp_session sess)
244{
245    zfree(sess, sizeof(struct tcp_session));
246
247    return 0;
248}
249
250static int
251zts_delete(Tcp_session sess)
252{
253    LinkNode node;
254
255    node = linknodebydatum(ztcp_sessions, (void *)sess);
256
257    if (!node)
258    {
259	return 1;
260    }
261
262    ztcp_free_session(getdata(node));
263    remnode(ztcp_sessions, node);
264
265    return 0;
266}
267
268static Tcp_session
269zts_byfd(int fd)
270{
271    LinkNode node;
272
273    for (node = firstnode(ztcp_sessions); node; incnode(node))
274	if (((Tcp_session)getdata(node))->fd == fd)
275	    return (Tcp_session)getdata(node);
276
277    return NULL;
278}
279
280static void
281tcp_cleanup(void)
282{
283    LinkNode node, next;
284
285    for (node = firstnode(ztcp_sessions); node; node = next) {
286	next = node->next;
287	tcp_close((Tcp_session)getdata(node));
288    }
289}
290
291/**/
292mod_export int
293tcp_close(Tcp_session sess)
294{
295    int err;
296
297    if (sess)
298    {
299	if (sess->fd != -1)
300	{
301	    err = close(sess->fd);
302	    if (err)
303		zwarn("connection close failed: %e", errno);
304	}
305	zts_delete(sess);
306	return 0;
307    }
308
309    return -1;
310}
311
312/**/
313mod_export int
314tcp_connect(Tcp_session sess, char *addrp, struct hostent *zhost, int d_port)
315{
316    int salen;
317#ifdef SUPPORT_IPV6
318    if (zhost->h_addrtype==AF_INET6) {
319	memcpy(&(sess->peer.in6.sin6_addr), addrp, zhost->h_length);
320	sess->peer.in6.sin6_port = d_port;
321	sess->peer.in6.sin6_flowinfo = 0;
322# ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
323	sess->peer.in6.sin6_scope_id = 0;
324# endif
325	sess->peer.in6.sin6_family = zhost->h_addrtype;
326	salen = sizeof(struct sockaddr_in6);
327    } else
328#endif /* SUPPORT_IPV6 */
329    {
330	memcpy(&(sess->peer.in.sin_addr), addrp, zhost->h_length);
331	sess->peer.in.sin_port = d_port;
332	sess->peer.in.sin_family = zhost->h_addrtype;
333	salen = sizeof(struct sockaddr_in);
334    }
335
336    return connect(sess->fd, (struct sockaddr *)&(sess->peer), salen);
337}
338
339static int
340bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
341{
342    int herrno, err=1, destport, force=0, verbose=0, test=0, targetfd=0;
343    ZSOCKLEN_T  len;
344    char **addrp, *desthost, *localname, *remotename;
345    struct hostent *zthost = NULL, *ztpeer = NULL;
346    struct servent *srv;
347    Tcp_session sess = NULL;
348
349    if (OPT_ISSET(ops,'f'))
350	force = 1;
351
352    if (OPT_ISSET(ops,'v'))
353	verbose = 1;
354
355    if (OPT_ISSET(ops,'t'))
356        test = 1;
357
358    if (OPT_ISSET(ops,'d')) {
359	targetfd = atoi(OPT_ARG(ops,'d'));
360	if (!targetfd) {
361	    zwarnnam(nam, "%s is an invalid argument to -d", OPT_ARG(ops,'d'));
362	    return 1;
363	}
364    }
365
366
367    if (OPT_ISSET(ops,'c')) {
368	if (!args[0]) {
369	    tcp_cleanup();
370	}
371	else {
372	    targetfd = atoi(args[0]);
373	    sess = zts_byfd(targetfd);
374	    if(!targetfd) {
375		zwarnnam(nam, "%s is an invalid argument to -c", args[0]);
376		return 1;
377	    }
378
379	    if (sess)
380	    {
381		if ((sess->flags & ZTCP_ZFTP) && !force)
382		{
383		    zwarnnam(nam, "use -f to force closure of a zftp control connection");
384		    return 1;
385		}
386		tcp_close(sess);
387		return 0;
388	    }
389	    else
390	    {
391		zwarnnam(nam, "fd %s not found in tcp table", args[0]);
392		return 1;
393	    }
394	}
395    }
396    else if (OPT_ISSET(ops,'l')) {
397	int lport = 0;
398
399	if (!args[0]) {
400	    zwarnnam(nam, "-l requires an argument");
401	    return 1;
402	}
403
404	srv = getservbyname(args[0], "tcp");
405	if (srv)
406	    lport = srv->s_port;
407	else
408	    lport = htons(atoi(args[0]));
409	if (!lport) { zwarnnam(nam, "bad service name or port number");
410	return 1;
411	}
412	sess = tcp_socket(PF_INET, SOCK_STREAM, 0, ZTCP_LISTEN);
413
414	if (!sess) {
415	    zwarnnam(nam, "unable to allocate a TCP session slot");
416	    return 1;
417	}
418#ifdef SO_OOBINLINE
419	len = 1;
420	setsockopt(sess->fd, SOL_SOCKET, SO_OOBINLINE, (char *)&len, sizeof(len));
421#endif
422	if (!zsh_inet_aton("0.0.0.0", &(sess->sock.in.sin_addr)))
423	{
424	    zwarnnam(nam, "bad address: %s", "0.0.0.0");
425	    return 1;
426	}
427
428	sess->sock.in.sin_family = AF_INET;
429	sess->sock.in.sin_port = lport;
430
431
432	if (bind(sess->fd, (struct sockaddr *)&sess->sock.in, sizeof(struct sockaddr_in)))
433	{
434	    char buf[DIGBUFSIZE];
435	    convbase(buf, (zlong)ntohs(lport), 10);
436	    zwarnnam(nam, "could not bind to port %s: %e", buf, errno);
437	    tcp_close(sess);
438	    return 1;
439	}
440
441	if (listen(sess->fd, 1))
442	{
443	    zwarnnam(nam, "could not listen on socket: %e", errno);
444	    tcp_close(sess);
445	    return 1;
446	}
447
448	if (targetfd) {
449	    sess->fd = redup(sess->fd, targetfd);
450	}
451	else {
452	    /* move the fd since no one will want to read from it */
453	    sess->fd = movefd(sess->fd);
454	}
455
456	if (sess->fd == -1) {
457	    zwarnnam(nam, "cannot duplicate fd %d: %e", sess->fd, errno);
458	    tcp_close(sess);
459	    return 1;
460	}
461
462	setiparam("REPLY", sess->fd);
463
464	if (verbose)
465	    printf("%d listener is on fd %d\n", ntohs(sess->sock.in.sin_port), sess->fd);
466
467	return 0;
468
469    }
470    else if (OPT_ISSET(ops,'a'))
471    {
472	int lfd, rfd;
473
474	if (!args[0]) {
475	    zwarnnam(nam, "-a requires an argument");
476	    return 1;
477	}
478
479	lfd = atoi(args[0]);
480
481	if (!lfd) {
482	    zwarnnam(nam, "invalid numerical argument");
483	    return 1;
484	}
485
486	sess = zts_byfd(lfd);
487	if (!sess) {
488	    zwarnnam(nam, "fd %s is not registered as a tcp connection", args[0]);
489	    return 1;
490	}
491
492	if (!(sess->flags & ZTCP_LISTEN))
493	{
494	    zwarnnam(nam, "tcp connection not a listener");
495	    return 1;
496	}
497
498	if (test) {
499#if defined(HAVE_POLL) || defined(HAVE_SELECT)
500# ifdef HAVE_POLL
501	    struct pollfd pfd;
502	    int ret;
503
504	    pfd.fd = lfd;
505	    pfd.events = POLLIN;
506	    if ((ret = poll(&pfd, 1, 0)) == 0) return 1;
507	    else if (ret == -1)
508	    {
509		zwarnnam(nam, "poll error: %e", errno);
510		return 1;
511	    }
512# else
513	    fd_set rfds;
514	    struct timeval tv;
515	    int ret;
516
517	    FD_ZERO(&rfds);
518	    FD_SET(lfd, &rfds);
519	    tv.tv_sec = 0;
520	    tv.tv_usec = 0;
521
522	    if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1;
523	    else if (ret == -1)
524	    {
525		zwarnnam(nam, "select error: %e", errno);
526		return 1;
527	    }
528
529# endif
530
531#else
532	    zwarnnam(nam, "not currently supported");
533	    return 1;
534#endif
535	}
536	sess = zts_alloc(ZTCP_INBOUND);
537
538	len = sizeof(sess->peer.in);
539	if ((rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len)) == -1)
540	{
541	    zwarnnam(nam, "could not accept connection: %e", errno);
542	    tcp_close(sess);
543	    return 1;
544	}
545
546	if (targetfd) {
547	    sess->fd = redup(rfd, targetfd);
548	    if (sess->fd < 0) {
549		zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno);
550		return 1;
551	    }
552	}
553	else {
554	    sess->fd = rfd;
555	}
556
557	setiparam("REPLY", sess->fd);
558
559	if (verbose)
560	    printf("%d is on fd %d\n", ntohs(sess->peer.in.sin_port), sess->fd);
561    }
562    else
563    {
564	if (!args[0]) {
565	    LinkNode node;
566	    for(node = firstnode(ztcp_sessions); node; incnode(node))
567	    {
568		sess = (Tcp_session)getdata(node);
569
570		if (sess->fd != -1)
571		{
572		    zthost = gethostbyaddr((const void *)&(sess->sock.in.sin_addr), sizeof(sess->sock.in.sin_addr), AF_INET);
573		    if (zthost)
574			localname = zthost->h_name;
575		    else
576			localname = inet_ntoa(sess->sock.in.sin_addr);
577		    ztpeer = gethostbyaddr((const void *)&(sess->peer.in.sin_addr), sizeof(sess->peer.in.sin_addr), AF_INET);
578		    if (ztpeer)
579			remotename = ztpeer->h_name;
580		    else
581			remotename = inet_ntoa(sess->peer.in.sin_addr);
582		    if (OPT_ISSET(ops,'L')) {
583			int schar;
584			if (sess->flags & ZTCP_ZFTP)
585			    schar = 'Z';
586			else if (sess->flags & ZTCP_LISTEN)
587			    schar = 'L';
588			else if (sess->flags & ZTCP_INBOUND)
589			    schar = 'I';
590			else
591			    schar = 'O';
592			printf("%d %c %s %d %s %d\n",
593			       sess->fd, schar,
594			       localname, ntohs(sess->sock.in.sin_port),
595			       remotename, ntohs(sess->peer.in.sin_port));
596		    } else {
597			printf("%s:%d %s %s:%d is on fd %d%s\n",
598			       localname, ntohs(sess->sock.in.sin_port),
599			       ((sess->flags & ZTCP_LISTEN) ? "-<" :
600				((sess->flags & ZTCP_INBOUND) ? "<-" : "->")),
601			       remotename, ntohs(sess->peer.in.sin_port),
602			       sess->fd,
603			       (sess->flags & ZTCP_ZFTP) ? " ZFTP" : "");
604		    }
605		}
606	    }
607	    return 0;
608	}
609	else if (!args[1]) {
610	    destport = htons(23);
611	}
612	else {
613
614	    srv = getservbyname(args[1],"tcp");
615	    if (srv)
616		destport = srv->s_port;
617	    else
618		destport = htons(atoi(args[1]));
619	}
620
621	desthost = ztrdup(args[0]);
622
623	zthost = zsh_getipnodebyname(desthost, AF_INET, 0, &herrno);
624	if (!zthost || errflag) {
625	    zwarnnam(nam, "host resolution failure: %s", desthost);
626	    return 1;
627	}
628
629	sess = tcp_socket(PF_INET, SOCK_STREAM, 0, 0);
630
631	if (!sess) {
632	    zwarnnam(nam, "unable to allocate a TCP session slot");
633	    return 1;
634	}
635
636#ifdef SO_OOBINLINE
637	len = 1;
638	setsockopt(sess->fd, SOL_SOCKET, SO_OOBINLINE, (char *)&len, sizeof(len));
639#endif
640
641	if (sess->fd < 0) {
642	    zwarnnam(nam, "socket creation failed: %e", errno);
643	    zsfree(desthost);
644	    zts_delete(sess);
645	    return 1;
646	}
647
648	for (addrp = zthost->h_addr_list; err && *addrp; addrp++) {
649	    if (zthost->h_length != 4)
650		zwarnnam(nam, "address length mismatch");
651	    do {
652		err = tcp_connect(sess, *addrp, zthost, destport);
653	    } while (err && errno == EINTR && !errflag);
654	}
655
656	if (err) {
657	    zwarnnam(nam, "connection failed: %e", errno);
658	    tcp_close(sess);
659	    zsfree(desthost);
660	    return 1;
661	}
662	else
663	{
664	    if (targetfd) {
665		sess->fd = redup(sess->fd, targetfd);
666		if (sess->fd < 0) {
667		    zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno);
668		    return 1;
669		}
670	    }
671
672	    setiparam("REPLY", sess->fd);
673
674	    if (verbose)
675		printf("%s:%d is now on fd %d\n",
676			desthost, destport, sess->fd);
677	}
678
679	zsfree(desthost);
680    }
681
682    return 0;
683}
684
685static struct builtin bintab[] = {
686    BUILTIN("ztcp", 0, bin_ztcp, 0, 3, 0, "acd:flLtv", NULL),
687};
688
689static struct features module_features = {
690    bintab, sizeof(bintab)/sizeof(*bintab),
691    NULL, 0,
692    NULL, 0,
693    NULL, 0,
694    0
695};
696
697/* The load/unload routines required by the zsh library interface */
698
699/**/
700int
701setup_(UNUSED(Module m))
702{
703    return 0;
704}
705
706/**/
707int
708features_(Module m, char ***features)
709{
710    *features = featuresarray(m, &module_features);
711    return 0;
712}
713
714/**/
715int
716enables_(Module m, int **enables)
717{
718    return handlefeatures(m, &module_features, enables);
719}
720
721/**/
722int
723boot_(Module m)
724{
725    ztcp_sessions = znewlinklist();
726    return 0;
727}
728
729
730/**/
731int
732cleanup_(Module m)
733{
734    tcp_cleanup();
735    freelinklist(ztcp_sessions, (FreeFunc) ztcp_free_session);
736    return setfeatureenables(m, &module_features, NULL);
737}
738
739/**/
740int
741finish_(UNUSED(Module m))
742{
743    return 0;
744}
745