2001-06-06 20:53:56 +02:00
/*
2001-09-08 23:09:55 +02:00
* tcp . c - TCP module
2001-06-06 20:53:56 +02:00
*
* 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"
/*
* For some reason , configure doesn ' t always detect netinet / in_systm . h .
* On some systems , including linux , this seems to be because gcc is
* throwing up a warning message about the redefinition of
* __USE_LARGEFILE . This means the problem is somewhere in the
* header files where we can ' t get at it . For now , revert to
* not including this file only on systems where we know it ' s missing .
* Currently this is just cygwin .
*/
# ifndef __CYGWIN__
# include <netinet / in_systm.h>
# endif
# include <netinet/in.h>
# include <netinet/ip.h>
# include <arpa/inet.h>
/* it's a TELNET based protocol, but don't think I like doing this */
# include <arpa/telnet.h>
/*
* 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 )
{
2001-09-09 11:39:24 +02:00
if ( af ! = AF_INET ) {
2001-09-08 23:09:55 +02:00
errno = EAFNOSUPPORT ;
return NULL ;
}
2001-09-09 11:39:24 +02:00
if ( len < INET_ADDRSTRLEN ) {
2001-09-08 23:09:55 +02:00
errno = ENOSPC ;
return NULL ;
}
strcpy ( buf , inet_ntoa ( * ( struct in_addr * ) cp ) ) ;
return buf ;
2001-06-06 20:53:56 +02:00
}
/**/
# else /* !HAVE_INET_NTOP */
/**/
# define zsh_inet_ntop inet_ntop
/**/
# endif /* !HAVE_INET_NTOP */
/**/
2001-09-09 11:39:24 +02:00
# ifndef HAVE_INET_ATON
2001-06-06 20:53:56 +02:00
2001-09-09 11:39:24 +02:00
# ifndef INADDR_NONE
# define INADDR_NONE 0xffffffffUL
# endif
2001-06-06 20:53:56 +02:00
/**/
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
/**/
2001-09-09 11:39:24 +02:00
# endif /* !HAVE_INET_ATON */
/**/
# ifndef HAVE_INET_PTON
2001-06-06 20:53:56 +02:00
/**/
mod_export int
zsh_inet_pton ( int af , char const * src , void * dst )
{
2001-09-09 11:39:24 +02:00
if ( af ! = AF_INET ) {
2001-09-08 23:09:55 +02:00
errno = EAFNOSUPPORT ;
return - 1 ;
}
return ! ! zsh_inet_aton ( src , dst ) ;
2001-06-06 20:53:56 +02:00
}
# 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 )
{
2001-09-09 11:39:24 +02:00
if ( af ! = AF_INET ) {
2001-09-08 23:09:55 +02:00
h_errno = NO_RECOVERY ;
return NULL ;
}
return gethostbyname ( name ) ;
2001-06-06 20:53:56 +02:00
}
/**/
# 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 , int flags , int * errorp )
{
2001-09-08 23:09:55 +02:00
static struct hostent ahe ;
static char nbuf [ 16 ] ;
static char * addrlist [ ] = { nbuf , NULL } ;
2001-06-06 20:53:56 +02:00
# ifdef SUPPORT_IPV6
2001-09-08 23:09:55 +02:00
static char pbuf [ INET6_ADDRSTRLEN ] ;
2001-06-06 20:53:56 +02:00
# else
2001-09-08 23:09:55 +02:00
static char pbuf [ INET_ADDRSTRLEN ] ;
2001-06-06 20:53:56 +02:00
# endif
2001-09-08 23:09:55 +02:00
struct hostent * he ;
2001-09-09 11:39:24 +02:00
if ( zsh_inet_pton ( af , name , nbuf ) = = 1 ) {
2001-09-08 23:09:55 +02:00
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 ) ;
2001-09-09 11:39:24 +02:00
if ( ! he )
2001-09-08 23:09:55 +02:00
* errorp = h_errno ;
return he ;
2001-06-06 20:53:56 +02:00
}
/**/
mod_export void
freehostent ( struct hostent * ptr )
{
}
/**/
# else /* !HAVE_GETIPNODEBYNAME */
/**/
# define zsh_getipnodebyname getipnodebyname
/**/
# endif /* !HAVE_GETIPNODEBYNAME */
2001-09-08 23:09:55 +02:00
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 ) ) ;
2001-09-09 11:39:24 +02:00
if ( ! sess ) return NULL ;
2001-09-08 23:09:55 +02:00
sess - > fd = - 1 ;
sess - > next = NULL ;
sess - > flags = ztflags ;
2001-09-09 11:39:24 +02:00
if ( ! zts_head ( ) ) {
2001-09-08 23:09:55 +02:00
ztcp_head = ztcp_tail = sess ;
}
else {
ztcp_tail - > next = sess ;
}
return sess ;
}
2001-06-06 20:53:56 +02:00
/**/
2001-09-08 23:09:55 +02:00
mod_export Tcp_session
tcp_socket ( int domain , int type , int protocol , int ztflags )
2001-06-06 20:53:56 +02:00
{
2001-09-08 23:09:55 +02:00
Tcp_session sess ;
sess = zts_alloc ( ztflags ) ;
2001-09-09 11:39:24 +02:00
if ( ! sess ) return NULL ;
2001-09-08 23:09:55 +02:00
2001-06-11 16:21:57 +02:00
sess - > fd = socket ( domain , type , protocol ) ;
2001-09-08 23:09:55 +02:00
return sess ;
}
static int
zts_delete ( Tcp_session sess )
{
Tcp_session tsess ;
tsess = zts_head ( ) ;
2001-09-09 11:39:24 +02:00
if ( tsess = = sess )
2001-09-08 23:09:55 +02:00
{
ztcp_head = sess - > next ;
free ( sess ) ;
return 0 ;
}
while ( ( tsess - > next ! = sess ) & & ( tsess - > next ) )
{
tsess = zts_next ( tsess ) ;
}
2001-09-09 11:39:24 +02:00
if ( ! tsess - > next ) return 1 ;
2001-09-08 23:09:55 +02:00
tsess - > next = tsess - > next - > next ;
free ( tsess - > next ) ;
return 0 ;
}
static Tcp_session
zts_byfd ( int fd )
{
Tcp_session tsess ;
tsess = zts_head ( ) ;
do {
2001-09-09 11:39:24 +02:00
if ( tsess - > fd = = fd )
2001-09-08 23:09:55 +02:00
return tsess ;
tsess = zts_next ( tsess ) ;
}
while ( tsess ! = NULL ) ;
return NULL ;
2001-06-06 20:53:56 +02:00
}
static void
tcp_cleanup ( void )
{
2001-09-08 23:09:55 +02:00
Tcp_session sess , prev ;
for ( sess = zts_head ( ) ; sess ! = NULL ; sess = zts_next ( prev ) )
{
prev = sess ;
tcp_close ( sess ) ;
zts_delete ( sess ) ;
}
2001-06-06 20:53:56 +02:00
}
2001-06-11 16:21:57 +02:00
/**/
mod_export int
tcp_close ( Tcp_session sess )
{
2001-09-08 23:09:55 +02:00
int err ;
2001-09-09 11:39:24 +02:00
if ( sess - > fd ! = - 1 )
2001-09-08 23:09:55 +02:00
{
err = close ( sess - > fd ) ;
2001-09-09 11:39:24 +02:00
if ( err )
2001-09-08 23:09:55 +02:00
{
zwarn ( " connection close failed: %e " , NULL , errno ) ;
return - 1 ;
}
2001-06-12 17:57:25 +02:00
return 0 ;
}
2001-09-08 23:09:55 +02:00
return - 1 ;
2001-06-12 17:57:25 +02:00
}
/**/
mod_export int
tcp_connect ( Tcp_session sess , char * addrp , struct hostent * zhost , int d_port )
{
2001-06-15 15:01:42 +02:00
int salen ;
2001-06-12 17:57:25 +02:00
# ifdef SUPPORT_IPV6
2001-09-09 11:39:24 +02:00
if ( zhost - > h_addrtype = = AF_INET6 ) {
2001-06-12 17:57:25 +02:00
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
2001-06-15 15:01:42 +02:00
salen = sizeof ( struct sockaddr_in6 ) ;
2001-06-12 17:57:25 +02:00
} else
# endif /* SUPPORT_IPV6 */
{
memcpy ( & ( sess - > peer . in . sin_addr ) , addrp , zhost - > h_length ) ;
sess - > peer . in . sin_port = d_port ;
2001-09-08 23:09:55 +02:00
sess - > peer . a . sa_family = zhost - > h_addrtype ;
2001-06-15 15:01:42 +02:00
salen = sizeof ( struct sockaddr_in ) ;
2001-06-12 17:57:25 +02:00
}
2001-06-15 15:01:42 +02:00
return connect ( sess - > fd , ( struct sockaddr * ) & ( sess - > peer ) , salen ) ;
2001-06-11 16:21:57 +02:00
}
2001-09-08 23:09:55 +02:00
static int
bin_ztcp ( char * nam , char * * args , char * ops , int func )
{
2001-09-09 11:39:24 +02:00
int herrno , err = 1 , destport , force = 0 , verbose = 0 , len , rfd ;
char * * addrp , * desthost , * localname , * remotename ;
struct hostent * zthost = NULL , * ztpeer = NULL ;
2001-09-08 23:09:55 +02:00
Tcp_session sess ;
if ( ops [ ' f ' ] )
force = 1 ;
2001-09-09 00:08:04 +02:00
if ( ops [ ' v ' ] )
verbose = 1 ;
2001-09-08 23:09:55 +02:00
if ( ops [ ' c ' ] ) {
if ( ! args [ 0 ] ) {
tcp_cleanup ( ) ;
}
else {
int targetfd = atoi ( args [ 0 ] ) ;
sess = zts_byfd ( targetfd ) ;
2001-09-09 11:39:24 +02:00
if ( sess )
2001-09-08 23:09:55 +02:00
{
2001-09-09 11:39:24 +02:00
if ( ( sess - > flags & ZTCP_ZFTP ) & & ! force )
2001-09-08 23:09:55 +02:00
{
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 ;
}
}
}
2001-09-09 11:39:24 +02:00
else if ( ops [ ' l ' ] ) {
int lport ;
if ( ! args [ 0 ] ) {
zwarnnam ( nam , " -l requires an argument " , NULL , 0 ) ;
return 1 ;
}
lport = atoi ( args [ 0 ] ) ;
if ( ! lport ) {
zwarnnam ( nam , " bad port number " , NULL , 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 ( ! 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 = htons ( lport ) ;
if ( bind ( sess - > fd , ( struct sockaddr * ) & sess - > sock . in , sizeof ( struct sockaddr_in ) ) )
{
zwarnnam ( nam , " could not bind to %s: %e " , " 0.0.0.0 " , 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 ( ( rfd = accept ( sess - > fd , ( struct sockaddr * ) & sess - > peer . in , & len ) ) = = - 1 )
{
zwarnnam ( nam , " could not accept connection: %e " , NULL , errno ) ;
tcp_close ( sess ) ;
return 1 ;
}
/* move the fd since it doesn't seem to be closing well */
sess - > fd = movefd ( sess - > fd ) ;
err = close ( sess - > fd ) ;
if ( err )
{
zwarn ( " listener close failed: %e " , NULL , errno ) ;
return - 1 ;
}
sess - > fd = rfd ;
fprintf ( shout , " %d is on fd %d \n " , ntohs ( sess - > peer . in . sin_port ) , sess - > fd ) ;
return 0 ;
}
2001-09-08 23:09:55 +02:00
else {
if ( ! args [ 0 ] ) {
for ( sess = zts_head ( ) ; sess ! = NULL ; sess = zts_next ( sess ) )
{
2001-09-09 11:39:24 +02:00
if ( sess - > fd ! = - 1 )
2001-09-08 23:09:55 +02:00
{
2001-09-09 11:39:24 +02:00
zthost = gethostbyaddr ( & ( sess - > sock . in . sin_addr ) , sizeof ( struct sockaddr_in ) , AF_INET ) ;
if ( zthost )
localname = zthost - > h_name ;
else
localname = ztrdup ( inet_ntoa ( sess - > sock . in . sin_addr ) ) ;
ztpeer = gethostbyaddr ( & ( sess - > peer . in . sin_addr ) , sizeof ( struct sockaddr_in ) , AF_INET ) ;
if ( ztpeer )
remotename = ztpeer - > h_name ;
else
remotename = ztrdup ( inet_ntoa ( sess - > sock . in . sin_addr ) ) ;
fprintf ( shout , " %s:%d -> %s:%d is on fd %d%s%s \n " , localname , ntohs ( sess - > sock . in . sin_port ) , remotename , ntohs ( sess - > peer . in . sin_port ) , sess - > fd , ( sess - > flags & ZTCP_ZFTP ) ? " ZFTP " : " " , ( sess - > flags & ZTCP_INBOUND ) ? " INBOUND " : " " ) ;
2001-09-08 23:09:55 +02:00
}
}
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 ) ;
2001-09-09 11:39:24 +02:00
if ( ! sess ) {
zwarnnam ( nam , " unable to allocate a TCP session slot " , NULL , 0 ) ;
return 1 ;
}
2001-09-08 23:09:55 +02:00
# 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 + + ) {
2001-09-09 11:39:24 +02:00
if ( zthost - > h_length ! = 4 )
2001-09-08 23:09:55 +02:00
zwarnnam ( nam , " address length mismatch " , NULL , 0 ) ;
do {
err = tcp_connect ( sess , * addrp , zthost , htons ( destport ) ) ;
} while ( err & & errno = = EINTR & & ! errflag ) ;
}
2001-09-09 11:39:24 +02:00
if ( err )
2001-09-08 23:09:55 +02:00
zwarnnam ( nam , " connection failed: %e " , NULL , errno ) ;
else
{
2001-09-09 11:39:24 +02:00
if ( verbose )
2001-09-09 00:08:04 +02:00
fprintf ( shout , " %s:%d is now on fd %d \n " , desthost , destport , sess - > fd ) ;
else
fprintf ( shout , " %d \n " , sess - > fd ) ;
2001-09-08 23:09:55 +02:00
}
zsfree ( desthost ) ;
}
return 0 ;
}
static struct builtin bintab [ ] = {
2001-09-09 11:39:24 +02:00
BUILTIN ( " ztcp " , 0 , bin_ztcp , 0 , 2 , 0 , " cflv " , NULL ) ,
2001-09-08 23:09:55 +02:00
} ;
2001-06-06 20:53:56 +02:00
/* The load/unload routines required by the zsh library interface */
/**/
int
setup_ ( Module m )
{
return 0 ;
}
/**/
int
boot_ ( Module m )
{
2001-09-08 23:09:55 +02:00
return ! addbuiltins ( m - > nam , bintab , sizeof ( bintab ) / sizeof ( * bintab ) ) ;
2001-06-06 20:53:56 +02:00
}
2001-09-08 23:09:55 +02:00
2001-06-06 20:53:56 +02:00
/**/
int
cleanup_ ( Module m )
{
tcp_cleanup ( ) ;
return 0 ;
}
/**/
int
finish_ ( Module m )
{
return 0 ;
}