2008-01-28 16:37:16 -05:00
/* source: socat.c */
2008-02-01 16:26:01 -05:00
/* Copyright Gerhard Rieger 2001-2008 */
2008-01-27 07:00:08 -05:00
/* Published under the GNU General Public License V.2, see file COPYING */
/* this is the main source, including command line option parsing, general
control , and the data shuffler */
# include "config.h"
# include "xioconfig.h" /* what features are enabled */
# include "sysincludes.h"
# include "mytypes.h"
# include "compat.h"
# include "error.h"
# include "sycls.h"
# include "sysutils.h"
# include "dalan.h"
# include "filan.h"
# include "xio.h"
# include "xioopts.h"
# include "xiolockfile.h"
/* command line options */
struct {
size_t bufsiz ;
bool verbose ;
bool verbhex ;
struct timeval pollintv ; /* with ignoreeof, reread after seconds */
struct timeval closwait ; /* after close of x, die after seconds */
struct timeval total_timeout ; /* when nothing happens, die after seconds */
bool debug ;
bool strictopts ; /* stop on errors in address options */
char logopt ; /* y..syslog; s..stderr; f..file; m..mixed */
bool lefttoright ; /* first addr ro, second addr wo */
bool righttoleft ; /* first addr wo, second addr ro */
xiolock_t lock ; /* a lock file */
} socat_opts = {
8192 , /* bufsiz */
false , /* verbose */
false , /* verbhex */
{ 1 , 0 } , /* pollintv */
{ 0 , 500000 } , /* closwait */
{ 0 , 0 } , /* total_timeout */
0 , /* debug */
0 , /* strictopts */
' s ' , /* logopt */
false , /* lefttoright */
false , /* righttoleft */
{ NULL , 0 } , /* lock */
} ;
void socat_usage ( FILE * fd ) ;
void socat_version ( FILE * fd ) ;
int socat ( const char * address1 , const char * address2 ) ;
int _socat ( void ) ;
int cv_newline ( unsigned char * * buff , ssize_t * bytes , int lineterm1 , int lineterm2 ) ;
void socat_signal ( int sig ) ;
static int socat_sigchild ( struct single * file ) ;
void lftocrlf ( char * * in , ssize_t * len , size_t bufsiz ) ;
void crlftolf ( char * * in , ssize_t * len , size_t bufsiz ) ;
static int socat_lock ( void ) ;
static void socat_unlock ( void ) ;
static int socat_newchild ( void ) ;
static const char socatversion [ ] =
# include "./VERSION"
;
static const char timestamp [ ] = __DATE__ " " __TIME__ ;
const char copyright_socat [ ] = " socat by Gerhard Rieger - see www.dest-unreach.org " ;
# if WITH_OPENSSL
const char copyright_openssl [ ] = " This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/) " ;
const char copyright_ssleay [ ] = " This product includes software written by Tim Hudson (tjh@cryptsoft.com) " ;
# endif
bool havelock ;
int main ( int argc , const char * argv [ ] ) {
const char * * arg1 , * a ;
2008-07-24 15:51:38 -04:00
char * mainwaitstring ;
2008-01-27 07:00:08 -05:00
char buff [ 10 ] ;
double rto ;
int i , argc0 , result ;
struct utsname ubuf ;
int lockrc ;
2008-07-24 15:51:38 -04:00
if ( mainwaitstring = getenv ( " SOCAT_MAIN_WAIT " ) ) {
sleep ( atoi ( mainwaitstring ) ) ;
}
2008-01-27 07:00:08 -05:00
diag_set ( ' p ' , strchr ( argv [ 0 ] , ' / ' ) ? strrchr ( argv [ 0 ] , ' / ' ) + 1 : argv [ 0 ] ) ;
/* we must init before applying options because env settings have lower
priority and are to be overridden by options */
if ( xioinitialize ( ) ! = 0 ) {
Exit ( 1 ) ;
}
xiosetopt ( ' p ' , " !! " ) ;
xiosetopt ( ' o ' , " : " ) ;
argc0 = argc ; /* save for later use */
arg1 = argv + 1 ; - - argc ;
while ( arg1 [ 0 ] & & ( arg1 [ 0 ] [ 0 ] = = ' - ' ) ) {
switch ( arg1 [ 0 ] [ 1 ] ) {
case ' V ' : socat_version ( stdout ) ; Exit ( 0 ) ;
# if WITH_HELP
case ' ? ' :
case ' h ' :
socat_usage ( stdout ) ;
xioopenhelp ( stdout , ( arg1 [ 0 ] [ 2 ] = = ' ? ' | | arg1 [ 0 ] [ 2 ] = = ' h ' ) ? ( arg1 [ 0 ] [ 3 ] = = ' ? ' | | arg1 [ 0 ] [ 3 ] = = ' h ' ) ? 2 : 1 : 0 ) ;
Exit ( 0 ) ;
# endif /* WITH_HELP */
case ' d ' : diag_set ( ' d ' , NULL ) ; break ;
# if WITH_FILAN
case ' D ' : socat_opts . debug = true ; break ;
# endif
case ' l ' :
switch ( arg1 [ 0 ] [ 2 ] ) {
case ' m ' : /* mixed mode: stderr, then switch to syslog; + facility */
diag_set ( ' s ' , NULL ) ;
xiosetopt ( ' l ' , " m " ) ;
socat_opts . logopt = arg1 [ 0 ] [ 2 ] ;
xiosetopt ( ' y ' , & arg1 [ 0 ] [ 3 ] ) ;
break ;
case ' y ' : /* syslog + facility */
diag_set ( arg1 [ 0 ] [ 2 ] , & arg1 [ 0 ] [ 3 ] ) ;
break ;
case ' f ' : /* to file, +filename */
case ' p ' : /* artificial program name */
if ( arg1 [ 0 ] [ 3 ] ) {
diag_set ( arg1 [ 0 ] [ 2 ] , & arg1 [ 0 ] [ 3 ] ) ;
} else if ( arg1 [ 1 ] ) {
diag_set ( arg1 [ 0 ] [ 2 ] , arg1 [ 1 ] ) ;
+ + arg1 , - - argc ;
} else {
Error1 ( " option -l%c requires an argument; use option \" -h \" for help " , arg1 [ 0 ] [ 2 ] ) ;
}
break ;
case ' s ' : /* stderr */
diag_set ( arg1 [ 0 ] [ 2 ] , NULL ) ;
break ;
case ' u ' :
diag_set ( ' u ' , NULL ) ;
break ;
case ' h ' :
diag_set_int ( ' h ' , true ) ;
break ;
default :
Error1 ( " unknown log option \" %s \" ; use option \" -h \" for help " , arg1 [ 0 ] ) ;
break ;
}
break ;
case ' v ' : socat_opts . verbose = true ; break ;
case ' x ' : socat_opts . verbhex = true ; break ;
case ' b ' : if ( arg1 [ 0 ] [ 2 ] ) {
a = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( a = * arg1 ) = = NULL ) {
Error ( " option -b requires an argument; use option \" -h \" for help " ) ;
Exit ( 1 ) ;
}
}
socat_opts . bufsiz = strtoul ( a , ( char * * ) & a , 0 ) ;
break ;
case ' s ' :
diag_set_int ( ' e ' , E_FATAL ) ; break ;
case ' t ' : if ( arg1 [ 0 ] [ 2 ] ) {
a = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( a = * arg1 ) = = NULL ) {
Error ( " option -t requires an argument; use option \" -h \" for help " ) ;
Exit ( 1 ) ;
}
}
rto = strtod ( a , ( char * * ) & a ) ;
socat_opts . closwait . tv_sec = rto ;
socat_opts . closwait . tv_usec =
( rto - socat_opts . closwait . tv_sec ) * 1000000 ;
break ;
case ' T ' : if ( arg1 [ 0 ] [ 2 ] ) {
a = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( a = * arg1 ) = = NULL ) {
Error ( " option -T requires an argument; use option \" -h \" for help " ) ;
Exit ( 1 ) ;
}
}
rto = strtod ( a , ( char * * ) & a ) ;
socat_opts . total_timeout . tv_sec = rto ;
socat_opts . total_timeout . tv_usec =
( rto - socat_opts . total_timeout . tv_sec ) * 1000000 ;
break ;
case ' u ' : socat_opts . lefttoright = true ; break ;
case ' U ' : socat_opts . righttoleft = true ; break ;
case ' g ' : xioopts_ignoregroups = true ; break ;
case ' L ' : if ( socat_opts . lock . lockfile )
Error ( " only one -L and -W option allowed " ) ;
if ( arg1 [ 0 ] [ 2 ] ) {
socat_opts . lock . lockfile = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( socat_opts . lock . lockfile = * arg1 ) = = NULL ) {
Error ( " option -L requires an argument; use option \" -h \" for help " ) ;
Exit ( 1 ) ;
}
}
break ;
case ' W ' : if ( socat_opts . lock . lockfile )
Error ( " only one -L and -W option allowed " ) ;
if ( arg1 [ 0 ] [ 2 ] ) {
socat_opts . lock . lockfile = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( socat_opts . lock . lockfile = * arg1 ) = = NULL ) {
Error ( " option -W requires an argument; use option \" -h \" for help " ) ;
Exit ( 1 ) ;
}
}
socat_opts . lock . waitlock = true ;
socat_opts . lock . intervall . tv_sec = 1 ;
socat_opts . lock . intervall . tv_nsec = 0 ;
break ;
# if WITH_IP4 || WITH_IP6
# if WITH_IP4
case ' 4 ' :
# endif
# if WITH_IP6
case ' 6 ' :
# endif
xioopts . default_ip = arg1 [ 0 ] [ 1 ] ;
xioopts . preferred_ip = arg1 [ 0 ] [ 1 ] ;
break ;
# endif /* WITH_IP4 || WITH_IP6 */
case ' \0 ' :
case ' , ' :
case ' : ' : break ; /* this "-" is a variation of STDIO */
default :
xioinqopt ( ' p ' , buff , sizeof ( buff ) ) ;
if ( arg1 [ 0 ] [ 1 ] = = buff [ 0 ] ) {
break ;
}
Error1 ( " unknown option \" %s \" ; use option \" -h \" for help " , arg1 [ 0 ] ) ;
Exit ( 1 ) ;
}
/* the leading "-" might be a form of the first address */
xioinqopt ( ' p ' , buff , sizeof ( buff ) ) ;
if ( arg1 [ 0 ] [ 0 ] = = ' - ' & &
( arg1 [ 0 ] [ 1 ] = = ' \0 ' | | arg1 [ 0 ] [ 1 ] = = ' : ' | |
arg1 [ 0 ] [ 1 ] = = ' , ' | | arg1 [ 0 ] [ 1 ] = = buff [ 0 ] ) )
break ;
+ + arg1 ; - - argc ;
}
if ( argc ! = 2 ) {
Error1 ( " exactly 2 addresses required (there are %d); use option \" -h \" for help " , argc ) ;
Exit ( 1 ) ;
}
if ( socat_opts . lefttoright & & socat_opts . righttoleft ) {
Error ( " -U and -u must not be combined " ) ;
}
Info ( copyright_socat ) ;
# if WITH_OPENSSL
Info ( copyright_openssl ) ;
Info ( copyright_ssleay ) ;
# endif
Debug2 ( " socat version %s on %s " , socatversion , timestamp ) ;
uname ( & ubuf ) ; /* ! here we circumvent internal tracing (Uname) */
Debug4 ( " running on %s version %s, release %s, machine %s \n " ,
ubuf . sysname , ubuf . version , ubuf . release , ubuf . machine ) ;
# if WITH_MSGLEVEL <= E_DEBUG
for ( i = 0 ; i < argc0 ; + + i ) {
Debug2 ( " argv[%d]: \" %s \" " , i , argv [ i ] ) ;
}
# endif /* WITH_MSGLEVEL <= E_DEBUG */
/* not sure what signal should print a message */
Signal ( SIGHUP , socat_signal ) ;
Signal ( SIGINT , socat_signal ) ;
Signal ( SIGQUIT , socat_signal ) ;
Signal ( SIGILL , socat_signal ) ;
/* SIGABRT for assert; catching caused endless recursion on assert in libc
( tzfile . c : 498 : __tzfile_compute : Assertion ' num_types = = 1 ' failed . ) */
/*Signal(SIGABRT, socat_signal);*/
Signal ( SIGBUS , socat_signal ) ;
Signal ( SIGFPE , socat_signal ) ;
Signal ( SIGSEGV , socat_signal ) ;
Signal ( SIGTERM , socat_signal ) ;
/* set xio hooks */
xiohook_newchild = & socat_newchild ;
if ( lockrc = socat_lock ( ) ) {
/* =0: goon; >0: locked; <0: error, printed in sub */
if ( lockrc > 0 )
Error1 ( " could not obtain lock \" %s \" " , socat_opts . lock . lockfile ) ;
Exit ( 1 ) ;
}
Atexit ( socat_unlock ) ;
result = socat ( arg1 [ 0 ] , arg1 [ 1 ] ) ;
Notice1 ( " exiting with status %d " , result ) ;
Exit ( result ) ;
return 0 ; /* not reached, just for gcc -Wall */
}
void socat_usage ( FILE * fd ) {
fputs ( copyright_socat , fd ) ; fputc ( ' \n ' , fd ) ;
fputs ( " Usage: \n " , fd ) ;
fputs ( " socat [options] <bi-address> <bi-address> \n " , fd ) ;
fputs ( " options: \n " , fd ) ;
fputs ( " -V print version and feature information to stdout, and exit \n " , fd ) ;
# if WITH_HELP
fputs ( " -h|-? print a help text describing command line options and addresses \n " , fd ) ;
fputs ( " -hh like -h, plus a list of all common address option names \n " , fd ) ;
fputs ( " -hhh like -hh, plus a list of all available address option names \n " , fd ) ;
# endif /* WITH_HELP */
fputs ( " -d increase verbosity (use up to 4 times; 2 are recommended) \n " , fd ) ;
# if WITH_FILAN
fputs ( " -D analyze file descriptors before loop \n " , fd ) ;
# endif
fputs ( " -ly[facility] log to syslog, using facility (default is daemon) \n " , fd ) ;
fputs ( " -lf<logfile> log to file \n " , fd ) ;
fputs ( " -ls log to stderr (default if no other log) \n " , fd ) ;
fputs ( " -lm[facility] mixed log mode (stderr during initialization, then syslog) \n " , fd ) ;
fputs ( " -lp<progname> set the program name used for logging \n " , fd ) ;
fputs ( " -lu use microseconds for logging timestamps \n " , fd ) ;
fputs ( " -lh add hostname to log messages \n " , fd ) ;
fputs ( " -v verbose data traffic, text \n " , fd ) ;
fputs ( " -x verbose data traffic, hexadecimal \n " , fd ) ;
fputs ( " -b<size_t> set data buffer size (8192) \n " , fd ) ;
fputs ( " -s sloppy (continue on error) \n " , fd ) ;
fputs ( " -t<timeout> wait seconds before closing second channel \n " , fd ) ;
fputs ( " -T<timeout> total inactivity timeout in seconds \n " , fd ) ;
fputs ( " -u unidirectional mode (left to right) \n " , fd ) ;
fputs ( " -U unidirectional mode (right to left) \n " , fd ) ;
fputs ( " -g do not check option groups \n " , fd ) ;
fputs ( " -L <lockfile> try to obtain lock, or fail \n " , fd ) ;
fputs ( " -W <lockfile> try to obtain lock, or wait \n " , fd ) ;
# if WITH_IP4
fputs ( " -4 prefer IPv4 if version is not explicitly specified \n " , fd ) ;
# endif
# if WITH_IP6
fputs ( " -6 prefer IPv6 if version is not explicitly specified \n " , fd ) ;
# endif
}
void socat_version ( FILE * fd ) {
struct utsname ubuf ;
fputs ( copyright_socat , fd ) ; fputc ( ' \n ' , fd ) ;
fprintf ( fd , " socat version %s on %s \n " , socatversion , timestamp ) ;
Uname ( & ubuf ) ;
fprintf ( fd , " running on %s version %s, release %s, machine %s \n " ,
ubuf . sysname , ubuf . version , ubuf . release , ubuf . machine ) ;
fputs ( " features: \n " , fd ) ;
# ifdef WITH_STDIO
fprintf ( fd , " #define WITH_STDIO %d \n " , WITH_STDIO ) ;
# else
fputs ( " #undef WITH_STDIO \n " , fd ) ;
# endif
# ifdef WITH_FDNUM
fprintf ( fd , " #define WITH_FDNUM %d \n " , WITH_FDNUM ) ;
# else
fputs ( " #undef WITH_FDNUM \n " , fd ) ;
# endif
# ifdef WITH_FILE
fprintf ( fd , " #define WITH_FILE %d \n " , WITH_FILE ) ;
# else
fputs ( " #undef WITH_FILE \n " , fd ) ;
# endif
# ifdef WITH_CREAT
fprintf ( fd , " #define WITH_CREAT %d \n " , WITH_CREAT ) ;
# else
fputs ( " #undef WITH_CREAT \n " , fd ) ;
# endif
# ifdef WITH_GOPEN
fprintf ( fd , " #define WITH_GOPEN %d \n " , WITH_GOPEN ) ;
# else
fputs ( " #undef WITH_GOPEN \n " , fd ) ;
# endif
# ifdef WITH_TERMIOS
fprintf ( fd , " #define WITH_TERMIOS %d \n " , WITH_TERMIOS ) ;
# else
fputs ( " #undef WITH_TERMIOS \n " , fd ) ;
# endif
# ifdef WITH_PIPE
fprintf ( fd , " #define WITH_PIPE %d \n " , WITH_PIPE ) ;
# else
fputs ( " #undef WITH_PIPE \n " , fd ) ;
# endif
# ifdef WITH_UNIX
fprintf ( fd , " #define WITH_UNIX %d \n " , WITH_UNIX ) ;
# else
fputs ( " #undef WITH_UNIX \n " , fd ) ;
# endif /* WITH_UNIX */
# ifdef WITH_ABSTRACT_UNIXSOCKET
fprintf ( fd , " #define WITH_ABSTRACT_UNIXSOCKET %d \n " , WITH_ABSTRACT_UNIXSOCKET ) ;
# else
fputs ( " #undef WITH_ABSTRACT_UNIXSOCKET \n " , fd ) ;
# endif /* WITH_ABSTRACT_UNIXSOCKET */
# ifdef WITH_IP4
fprintf ( fd , " #define WITH_IP4 %d \n " , WITH_IP4 ) ;
# else
fputs ( " #undef WITH_IP4 \n " , fd ) ;
# endif
# ifdef WITH_IP6
fprintf ( fd , " #define WITH_IP6 %d \n " , WITH_IP6 ) ;
# else
fputs ( " #undef WITH_IP6 \n " , fd ) ;
# endif
# ifdef WITH_RAWIP
fprintf ( fd , " #define WITH_RAWIP %d \n " , WITH_RAWIP ) ;
# else
fputs ( " #undef WITH_RAWIP \n " , fd ) ;
# endif
# ifdef WITH_TCP
fprintf ( fd , " #define WITH_TCP %d \n " , WITH_TCP ) ;
# else
fputs ( " #undef WITH_TCP \n " , fd ) ;
# endif
# ifdef WITH_UDP
fprintf ( fd , " #define WITH_UDP %d \n " , WITH_UDP ) ;
# else
fputs ( " #undef WITH_UDP \n " , fd ) ;
# endif
# ifdef WITH_LISTEN
fprintf ( fd , " #define WITH_LISTEN %d \n " , WITH_LISTEN ) ;
# else
fputs ( " #undef WITH_LISTEN \n " , fd ) ;
# endif
# ifdef WITH_SOCKS4
fprintf ( fd , " #define WITH_SOCKS4 %d \n " , WITH_SOCKS4 ) ;
# else
fputs ( " #undef WITH_SOCKS4 \n " , fd ) ;
# endif
# ifdef WITH_SOCKS4A
fprintf ( fd , " #define WITH_SOCKS4A %d \n " , WITH_SOCKS4A ) ;
# else
fputs ( " #undef WITH_SOCKS4A \n " , fd ) ;
# endif
# ifdef WITH_PROXY
fprintf ( fd , " #define WITH_PROXY %d \n " , WITH_PROXY ) ;
# else
fputs ( " #undef WITH_PROXY \n " , fd ) ;
# endif
# ifdef WITH_SYSTEM
fprintf ( fd , " #define WITH_SYSTEM %d \n " , WITH_SYSTEM ) ;
# else
fputs ( " #undef WITH_SYSTEM \n " , fd ) ;
# endif
# ifdef WITH_EXEC
fprintf ( fd , " #define WITH_EXEC %d \n " , WITH_EXEC ) ;
# else
fputs ( " #undef WITH_EXEC \n " , fd ) ;
# endif
# ifdef WITH_READLINE
fprintf ( fd , " #define WITH_READLINE %d \n " , WITH_READLINE ) ;
# else
fputs ( " #undef WITH_READLINE \n " , fd ) ;
# endif
# ifdef WITH_TUN
fprintf ( fd , " #define WITH_TUN %d \n " , WITH_TUN ) ;
# else
fputs ( " #undef WITH_TUN \n " , fd ) ;
# endif
# ifdef WITH_PTY
fprintf ( fd , " #define WITH_PTY %d \n " , WITH_PTY ) ;
# else
fputs ( " #undef WITH_PTY \n " , fd ) ;
# endif
# ifdef WITH_OPENSSL
fprintf ( fd , " #define WITH_OPENSSL %d \n " , WITH_OPENSSL ) ;
# else
fputs ( " #undef WITH_OPENSSL \n " , fd ) ;
# endif
# ifdef WITH_FIPS
fprintf ( fd , " #define WITH_FIPS %d \n " , WITH_FIPS ) ;
# else
fputs ( " #undef WITH_FIPS \n " , fd ) ;
# endif
# ifdef WITH_LIBWRAP
fprintf ( fd , " #define WITH_LIBWRAP %d \n " , WITH_LIBWRAP ) ;
# else
fputs ( " #undef WITH_LIBWRAP \n " , fd ) ;
# endif
# ifdef WITH_SYCLS
fprintf ( fd , " #define WITH_SYCLS %d \n " , WITH_SYCLS ) ;
# else
fputs ( " #undef WITH_SYCLS \n " , fd ) ;
# endif
# ifdef WITH_FILAN
fprintf ( fd , " #define WITH_FILAN %d \n " , WITH_FILAN ) ;
# else
fputs ( " #undef WITH_FILAN \n " , fd ) ;
# endif
# ifdef WITH_RETRY
fprintf ( fd , " #define WITH_RETRY %d \n " , WITH_RETRY ) ;
# else
fputs ( " #undef WITH_RETRY \n " , fd ) ;
# endif
# ifdef WITH_MSGLEVEL
fprintf ( fd , " #define WITH_MSGLEVEL %d /*%s*/ \n " , WITH_MSGLEVEL ,
& " debug \0 \0 \0 info \0 \0 \0 \0 notice \0 \0 warn \0 \0 \0 \0 error \0 \0 \0 fatal \0 \0 \0 " [ WITH_MSGLEVEL < < 3 ] ) ;
# else
fputs ( " #undef WITH_MSGLEVEL \n " , fd ) ;
# endif
}
xiofile_t * sock1 , * sock2 ;
int closing = 0 ; /* 0..no eof yet, 1..first eof just occurred,
2. . counting down closing timeout */
/* call this function when the common command line options are parsed, and the
addresses are extracted ( but not resolved ) . */
int socat ( const char * address1 , const char * address2 ) {
int mayexec ;
# if 1
if ( Signal ( SIGPIPE , SIG_IGN ) = = SIG_ERR ) {
Warn1 ( " signal(SIGPIPE, SIG_IGN): %s " , strerror ( errno ) ) ;
}
# endif
if ( socat_opts . lefttoright ) {
if ( ( sock1 = xioopen ( address1 , XIO_RDONLY | XIO_MAYFORK | XIO_MAYCHILD | XIO_MAYCONVERT ) ) = = NULL ) {
return - 1 ;
}
xiosetsigchild ( sock1 , socat_sigchild ) ;
} else if ( socat_opts . righttoleft ) {
if ( ( sock1 = xioopen ( address1 , XIO_WRONLY | XIO_MAYFORK | XIO_MAYCHILD | XIO_MAYCONVERT ) ) = = NULL ) {
return - 1 ;
}
xiosetsigchild ( sock1 , socat_sigchild ) ;
} else {
if ( ( sock1 = xioopen ( address1 , XIO_RDWR | XIO_MAYFORK | XIO_MAYCHILD | XIO_MAYCONVERT ) ) = = NULL ) {
return - 1 ;
}
xiosetsigchild ( sock1 , socat_sigchild ) ;
}
# if 1 /*! */
if ( XIO_READABLE ( sock1 ) & &
( XIO_RDSTREAM ( sock1 ) - > howtoend = = END_KILL | |
XIO_RDSTREAM ( sock1 ) - > howtoend = = END_CLOSE_KILL | |
XIO_RDSTREAM ( sock1 ) - > howtoend = = END_SHUTDOWN_KILL ) ) {
if ( XIO_RDSTREAM ( sock1 ) - > para . exec . pid = = diedunknown1 ) {
/* child has alread died... but it might have put regular data into
the communication channel , so continue */
Info1 ( " child " F_pid " has already died (diedunknown1) " ,
XIO_RDSTREAM ( sock1 ) - > para . exec . pid ) ;
diedunknown1 = 0 ;
XIO_RDSTREAM ( sock1 ) - > para . exec . pid = 0 ;
/* return STAT_RETRYLATER; */
} else if ( XIO_RDSTREAM ( sock1 ) - > para . exec . pid = = diedunknown2 ) {
Info1 ( " child " F_pid " has already died (diedunknown2) " ,
XIO_RDSTREAM ( sock1 ) - > para . exec . pid ) ;
diedunknown2 = 0 ;
XIO_RDSTREAM ( sock1 ) - > para . exec . pid = 0 ;
} else if ( XIO_RDSTREAM ( sock1 ) - > para . exec . pid = = diedunknown3 ) {
Info1 ( " child " F_pid " has already died (diedunknown3) " ,
XIO_RDSTREAM ( sock1 ) - > para . exec . pid ) ;
diedunknown3 = 0 ;
XIO_RDSTREAM ( sock1 ) - > para . exec . pid = 0 ;
} else if ( XIO_RDSTREAM ( sock1 ) - > para . exec . pid = = diedunknown4 ) {
Info1 ( " child " F_pid " has already died (diedunknown4) " ,
XIO_RDSTREAM ( sock1 ) - > para . exec . pid ) ;
diedunknown4 = 0 ;
XIO_RDSTREAM ( sock1 ) - > para . exec . pid = 0 ;
}
}
# endif
mayexec = ( sock1 - > common . flags & XIO_DOESCONVERT ? 0 : XIO_MAYEXEC ) ;
if ( XIO_WRITABLE ( sock1 ) ) {
if ( XIO_READABLE ( sock1 ) ) {
if ( ( sock2 = xioopen ( address2 , XIO_RDWR | XIO_MAYFORK | XIO_MAYCHILD | mayexec | XIO_MAYCONVERT ) ) = = NULL ) {
return - 1 ;
}
xiosetsigchild ( sock2 , socat_sigchild ) ;
} else {
if ( ( sock2 = xioopen ( address2 , XIO_RDONLY | XIO_MAYFORK | XIO_MAYCHILD | mayexec | XIO_MAYCONVERT ) ) = = NULL ) {
return - 1 ;
}
xiosetsigchild ( sock2 , socat_sigchild ) ;
}
} else { /* assuming sock1 is readable */
if ( ( sock2 = xioopen ( address2 , XIO_WRONLY | XIO_MAYFORK | XIO_MAYCHILD | mayexec | XIO_MAYCONVERT ) ) = = NULL ) {
return - 1 ;
}
xiosetsigchild ( sock2 , socat_sigchild ) ;
}
# if 1 /*! */
if ( XIO_READABLE ( sock2 ) & &
( XIO_RDSTREAM ( sock2 ) - > howtoend = = END_KILL | |
XIO_RDSTREAM ( sock2 ) - > howtoend = = END_CLOSE_KILL | |
XIO_RDSTREAM ( sock2 ) - > howtoend = = END_SHUTDOWN_KILL ) ) {
if ( XIO_RDSTREAM ( sock2 ) - > para . exec . pid = = diedunknown1 ) {
/* child has alread died... but it might have put regular data into
the communication channel , so continue */
Info1 ( " child " F_pid " has already died (diedunknown1) " ,
XIO_RDSTREAM ( sock2 ) - > para . exec . pid ) ;
diedunknown1 = 0 ;
XIO_RDSTREAM ( sock2 ) - > para . exec . pid = 0 ;
/* return STAT_RETRYLATER; */
} else if ( XIO_RDSTREAM ( sock2 ) - > para . exec . pid = = diedunknown2 ) {
Info1 ( " child " F_pid " has already died (diedunknown2) " ,
XIO_RDSTREAM ( sock2 ) - > para . exec . pid ) ;
diedunknown2 = 0 ;
XIO_RDSTREAM ( sock2 ) - > para . exec . pid = 0 ;
} else if ( XIO_RDSTREAM ( sock2 ) - > para . exec . pid = = diedunknown3 ) {
Info1 ( " child " F_pid " has already died (diedunknown3) " ,
XIO_RDSTREAM ( sock2 ) - > para . exec . pid ) ;
diedunknown3 = 0 ;
XIO_RDSTREAM ( sock2 ) - > para . exec . pid = 0 ;
} else if ( XIO_RDSTREAM ( sock2 ) - > para . exec . pid = = diedunknown4 ) {
Info1 ( " child " F_pid " has already died (diedunknown4) " ,
XIO_RDSTREAM ( sock2 ) - > para . exec . pid ) ;
diedunknown4 = 0 ;
XIO_RDSTREAM ( sock2 ) - > para . exec . pid = 0 ;
}
}
# endif
Info ( " resolved and opened all sock addresses " ) ;
return
_socat ( ) ; /* nsocks, sockets are visible outside function */
}
/* checks if this is a connection to a child process, and if so, sees if the
child already died , leaving some data for us .
returns < 0 if an error occurred ;
returns 0 if no child or not yet died or died without data ( sets eof ) ;
returns > 0 if child died and left data
*/
int childleftdata ( xiofile_t * xfd ) {
2008-07-24 15:51:38 -04:00
struct pollfd in ;
int timeout = 0 ; /* milliseconds */
2008-01-27 07:00:08 -05:00
int retval ;
2008-07-24 15:51:38 -04:00
2008-01-27 07:00:08 -05:00
/* have to check if a child process died before, but left read data */
if ( XIO_READABLE ( xfd ) & &
( XIO_RDSTREAM ( xfd ) - > howtoend = = END_KILL | |
XIO_RDSTREAM ( xfd ) - > howtoend = = END_CLOSE_KILL | |
XIO_RDSTREAM ( xfd ) - > howtoend = = END_SHUTDOWN_KILL ) & &
XIO_RDSTREAM ( xfd ) - > para . exec . pid = = 0 ) {
if ( XIO_READABLE ( xfd ) & & ! ( XIO_RDSTREAM ( xfd ) - > eof > = 2 & & ! XIO_RDSTREAM ( xfd ) - > ignoreeof ) ) {
2008-07-24 15:51:38 -04:00
in . fd = XIO_GETRDFD ( xfd ) ;
in . events = POLLIN /*|POLLRDBAND*/ ;
in . revents = 0 ;
2008-01-27 07:00:08 -05:00
}
do {
2008-07-24 15:51:38 -04:00
retval = xiopoll ( & in , 1 , timeout ) ;
2008-01-27 07:00:08 -05:00
} while ( retval < 0 & & errno = = EINTR ) ;
if ( retval < 0 ) {
2008-07-24 15:51:38 -04:00
Error4 ( " xiopoll({%d,%0o}, 1, %d): %s " ,
in . fd , in . events , timeout , strerror ( errno ) ) ;
2008-01-27 07:00:08 -05:00
return - 1 ;
2008-07-24 15:51:38 -04:00
}
if ( retval = = 0 ) {
2008-01-27 07:00:08 -05:00
Info ( " terminated child did not leave data for us " ) ;
XIO_RDSTREAM ( xfd ) - > eof = 2 ;
xfd - > stream . eof = 2 ;
closing = MAX ( closing , 1 ) ;
}
}
return 0 ;
}
int xiotransfer ( xiofile_t * inpipe , xiofile_t * outpipe ,
unsigned char * * buff , size_t bufsiz , bool righttoleft ) ;
bool mayrd1 ; /* sock1 has read data or eof, according to select() */
bool mayrd2 ; /* sock2 has read data or eof, according to select() */
bool maywr1 ; /* sock1 can be written to, according to select() */
bool maywr2 ; /* sock2 can be written to, according to select() */
/* here we come when the sockets are opened (in the meaning of C language),
and their options are set / applied
returns - 1 on error or 0 on success */
int _socat ( void ) {
2008-07-24 15:51:38 -04:00
struct pollfd fds [ 4 ] ,
* fd1in = & fds [ 0 ] ,
* fd1out = & fds [ 1 ] ,
* fd2in = & fds [ 2 ] ,
* fd2out = & fds [ 3 ] ;
2008-01-27 07:00:08 -05:00
int retval ;
unsigned char * buff ;
ssize_t bytes1 , bytes2 ;
int polling = 0 ; /* handling ignoreeof */
int wasaction = 1 ; /* last select was active, do NOT sleep before next */
struct timeval total_timeout ; /* the actual total timeout timer */
# if WITH_FILAN
if ( socat_opts . debug ) {
int fdi , fdo ;
int msglevel , exitlevel ;
msglevel = diag_get_int ( ' D ' ) ; /* save current message level */
diag_set_int ( ' D ' , E_ERROR ) ; /* only print errors and fatals in filan */
exitlevel = diag_get_int ( ' e ' ) ; /* save current exit level */
diag_set_int ( ' e ' , E_FATAL ) ; /* only exit on fatals */
fdi = XIO_GETRDFD ( sock1 ) ;
fdo = XIO_GETWRFD ( sock1 ) ;
filan_fd ( fdi , stderr ) ;
if ( fdo ! = fdi ) {
filan_fd ( fdo , stderr ) ;
}
fdi = XIO_GETRDFD ( sock2 ) ;
fdo = XIO_GETWRFD ( sock2 ) ;
filan_fd ( fdi , stderr ) ;
if ( fdo ! = fdi ) {
filan_fd ( fdo , stderr ) ;
}
diag_set_int ( ' e ' , exitlevel ) ; /* restore old exit level */
diag_set_int ( ' D ' , msglevel ) ; /* restore old message level */
}
# endif /* WITH_FILAN */
/* when converting nl to crnl, size might double */
buff = Malloc ( 2 * socat_opts . bufsiz + 1 ) ;
if ( buff = = NULL ) return - 1 ;
if ( socat_opts . logopt = = ' m ' & & xioinqopt ( ' l ' , NULL , 0 ) = = ' m ' ) {
Info ( " switching to syslog " ) ;
diag_set ( ' y ' , xioopts . syslogfac ) ;
xiosetopt ( ' l ' , " \0 " ) ;
}
total_timeout = socat_opts . total_timeout ;
Notice4 ( " starting data transfer loop with FDs [%d,%d] and [%d,%d] " ,
XIO_GETRDFD ( sock1 ) , XIO_GETWRFD ( sock1 ) ,
XIO_GETRDFD ( sock2 ) , XIO_GETWRFD ( sock2 ) ) ;
while ( XIO_RDSTREAM ( sock1 ) - > eof < = 1 | |
XIO_RDSTREAM ( sock2 ) - > eof < = 1 ) {
2008-07-24 15:51:38 -04:00
int timeout ;
2008-01-27 07:00:08 -05:00
Debug6 ( " data loop: sock1->eof=%d, sock2->eof=%d, closing=%d, wasaction=%d, total_to={ " F_tv_sec " . " F_tv_usec " } " ,
XIO_RDSTREAM ( sock1 ) - > eof , XIO_RDSTREAM ( sock2 ) - > eof ,
closing , wasaction ,
total_timeout . tv_sec , total_timeout . tv_usec ) ;
/* for ignoreeof */
if ( polling ) {
if ( ! wasaction ) {
/* yes we could do it with select but I like readable trace output */
if ( socat_opts . pollintv . tv_sec ) Sleep ( socat_opts . pollintv . tv_sec ) ;
if ( socat_opts . pollintv . tv_usec ) Usleep ( socat_opts . pollintv . tv_usec ) ;
if ( socat_opts . total_timeout . tv_sec ! = 0 | |
socat_opts . total_timeout . tv_usec ! = 0 ) {
if ( total_timeout . tv_usec < socat_opts . pollintv . tv_usec ) {
total_timeout . tv_usec + = 1000000 ;
total_timeout . tv_sec - = 1 ;
}
total_timeout . tv_sec - = socat_opts . pollintv . tv_sec ;
total_timeout . tv_usec - = socat_opts . pollintv . tv_usec ;
if ( total_timeout . tv_sec < 0 | |
total_timeout . tv_sec = = 0 & & total_timeout . tv_usec < 0 ) {
Notice ( " inactivity timeout triggered " ) ;
return 0 ;
}
}
} else {
wasaction = 0 ;
}
}
if ( polling ) {
/* there is a ignoreeof poll timeout, use it */
2008-07-24 15:51:38 -04:00
timeout = 1000 * socat_opts . pollintv . tv_sec +
socat_opts . pollintv . tv_usec / 1000 ; /*!!! overflow?*/
2008-01-27 07:00:08 -05:00
} else if ( socat_opts . total_timeout . tv_sec ! = 0 | |
socat_opts . total_timeout . tv_usec ! = 0 ) {
/* there might occur a total inactivity timeout */
2008-07-24 15:51:38 -04:00
timeout = 1000 * socat_opts . total_timeout . tv_sec +
socat_opts . total_timeout . tv_usec / 1000 ;
2008-01-27 07:00:08 -05:00
} else {
2008-07-24 15:51:38 -04:00
timeout = - 1 ; /* forever */
2008-01-27 07:00:08 -05:00
}
if ( closing > = 1 ) {
/* first eof already occurred, start end timer */
2008-07-24 15:51:38 -04:00
timeout = 1000 * socat_opts . closwait . tv_sec +
socat_opts . closwait . tv_usec / 1000 ;
2008-01-27 07:00:08 -05:00
closing = 2 ;
}
do {
int _errno ;
childleftdata ( sock1 ) ;
childleftdata ( sock2 ) ;
if ( closing > = 1 ) {
/* first eof already occurred, start end timer */
2008-07-24 15:51:38 -04:00
timeout = 1000 * socat_opts . closwait . tv_sec +
socat_opts . closwait . tv_usec / 1000 ;
2008-01-27 07:00:08 -05:00
closing = 2 ;
}
2008-07-24 15:51:38 -04:00
/* now the fds are assigned */
2008-01-27 07:00:08 -05:00
if ( XIO_READABLE ( sock1 ) & &
! ( XIO_RDSTREAM ( sock1 ) - > eof > 1 & & ! XIO_RDSTREAM ( sock1 ) - > ignoreeof ) & &
! socat_opts . righttoleft ) {
if ( ! mayrd1 ) {
2008-07-24 15:51:38 -04:00
fd1in - > fd = XIO_GETRDFD ( sock1 ) ;
fd1in - > events = POLLIN ;
} else {
fd1in - > fd = - 1 ;
2008-01-27 07:00:08 -05:00
}
if ( ! maywr2 ) {
2008-07-24 15:51:38 -04:00
fd2out - > fd = XIO_GETWRFD ( sock2 ) ;
fd2out - > events = POLLOUT ;
} else {
fd2out - > fd = - 1 ;
2008-01-27 07:00:08 -05:00
}
2008-07-24 15:51:38 -04:00
} else {
fd1in - > fd = - 1 ;
fd2out - > fd = - 1 ;
2008-01-27 07:00:08 -05:00
}
if ( XIO_READABLE ( sock2 ) & &
! ( XIO_RDSTREAM ( sock2 ) - > eof > 1 & & ! XIO_RDSTREAM ( sock2 ) - > ignoreeof ) & &
! socat_opts . lefttoright ) {
if ( ! mayrd2 ) {
2008-07-24 15:51:38 -04:00
fd2in - > fd = XIO_GETRDFD ( sock2 ) ;
fd2in - > events = POLLIN ;
} else {
fd2in - > fd = - 1 ;
2008-01-27 07:00:08 -05:00
}
if ( ! maywr1 ) {
2008-07-24 15:51:38 -04:00
fd1out - > fd = XIO_GETWRFD ( sock1 ) ;
fd1out - > events = POLLOUT ;
} else {
fd1out - > fd = - 1 ;
2008-01-27 07:00:08 -05:00
}
2008-07-24 15:51:38 -04:00
} else {
fd1out - > fd = - 1 ;
fd2in - > fd = - 1 ;
2008-01-27 07:00:08 -05:00
}
2008-07-24 15:51:38 -04:00
retval = xiopoll ( fds , 4 , timeout ) ;
2008-01-27 07:00:08 -05:00
_errno = errno ;
if ( retval < 0 & & errno = = EINTR ) {
Info1 ( " select(): %s " , strerror ( errno ) ) ;
}
errno = _errno ;
} while ( retval < 0 & & errno = = EINTR ) ;
/* attention:
when an exec ' d process sends data and terminates , it is unpredictable
whether the data or the sigchild arrives first .
*/
if ( retval < 0 ) {
2008-07-24 15:51:38 -04:00
Error10 ( " xiopoll({%d,%0o}{%d,%0o}{%d,%0o}{%d,%0o}, 4, %d): %s " ,
fds [ 0 ] . fd , fds [ 0 ] . events , fds [ 1 ] . fd , fds [ 1 ] . events ,
fds [ 2 ] . fd , fds [ 2 ] . events , fds [ 3 ] . fd , fds [ 3 ] . events ,
timeout , strerror ( errno ) ) ;
2008-01-27 07:00:08 -05:00
return - 1 ;
} else if ( retval = = 0 ) {
Info2 ( " select timed out (no data within %ld.%06ld seconds) " ,
closing > = 1 ? socat_opts . closwait . tv_sec : socat_opts . total_timeout . tv_sec ,
closing > = 1 ? socat_opts . closwait . tv_usec : socat_opts . total_timeout . tv_usec ) ;
if ( polling & & ! wasaction ) {
/* there was a ignoreeof poll timeout, use it */
;
} else if ( socat_opts . total_timeout . tv_sec ! = 0 | |
socat_opts . total_timeout . tv_usec ! = 0 ) {
/* there was a total inactivity timeout */
Notice ( " inactivity timeout triggered " ) ;
return 0 ;
}
if ( closing ) {
break ;
}
/* one possibility to come here is ignoreeof on some fd, but no EOF
and no data on any descriptor - this is no indication for end ! */
continue ;
}
if ( XIO_READABLE ( sock1 ) & & XIO_GETRDFD ( sock1 ) > = 0 & &
2008-07-24 15:51:38 -04:00
( fd1in - > revents & ( POLLIN | POLLHUP | POLLERR ) ) ) {
2008-01-27 07:00:08 -05:00
mayrd1 = true ;
}
if ( XIO_READABLE ( sock2 ) & & XIO_GETRDFD ( sock2 ) > = 0 & &
2008-07-24 15:51:38 -04:00
( fd2in - > revents & ( POLLIN | POLLHUP | POLLERR ) ) ) {
2008-01-27 07:00:08 -05:00
mayrd2 = true ;
}
2008-07-24 15:51:38 -04:00
if ( XIO_GETWRFD ( sock1 ) > = 0 & & ( fd1out - > revents & ( POLLOUT | POLLERR ) ) ) {
2008-01-27 07:00:08 -05:00
maywr1 = true ;
}
2008-07-24 15:51:38 -04:00
if ( XIO_GETWRFD ( sock2 ) > = 0 & & ( fd2out - > revents & ( POLLOUT | POLLERR ) ) ) {
2008-01-27 07:00:08 -05:00
maywr2 = true ;
}
if ( mayrd1 & & maywr2 ) {
mayrd1 = false ;
if ( ( bytes1 = xiotransfer ( sock1 , sock2 , & buff , socat_opts . bufsiz , false ) )
< 0 ) {
if ( errno ! = EAGAIN ) {
closing = MAX ( closing , 1 ) ;
Notice ( " socket 1 to socket 2 is in error " ) ;
if ( socat_opts . lefttoright ) {
break ;
}
}
} else if ( bytes1 > 0 ) {
maywr2 = false ;
total_timeout = socat_opts . total_timeout ;
wasaction = 1 ;
/* is more data available that has already passed select()? */
mayrd1 = ( xiopending ( sock1 ) > 0 ) ;
if ( XIO_RDSTREAM ( sock1 ) - > readbytes ! = 0 & &
XIO_RDSTREAM ( sock1 ) - > actbytes = = 0 ) {
/* avoid idle when all readbytes already there */
mayrd1 = true ;
}
}
/* (bytes1 == 0) handled later */
} else {
bytes1 = - 1 ;
}
if ( mayrd2 & & maywr1 ) {
mayrd2 = false ;
if ( ( bytes2 = xiotransfer ( sock2 , sock1 , & buff , socat_opts . bufsiz , true ) )
< 0 ) {
if ( errno ! = EAGAIN ) {
closing = MAX ( closing , 1 ) ;
Notice ( " socket 2 to socket 1 is in error " ) ;
if ( socat_opts . righttoleft ) {
break ;
}
}
} else if ( bytes2 > 0 ) {
maywr1 = false ;
total_timeout = socat_opts . total_timeout ;
wasaction = 1 ;
/* is more data available that has already passed select()? */
mayrd2 = ( xiopending ( sock2 ) > 0 ) ;
if ( XIO_RDSTREAM ( sock2 ) - > readbytes ! = 0 & &
XIO_RDSTREAM ( sock2 ) - > actbytes = = 0 ) {
/* avoid idle when all readbytes already there */
mayrd2 = true ;
}
}
/* (bytes2 == 0) handled later */
} else {
bytes2 = - 1 ;
}
/* NOW handle EOFs */
if ( bytes1 = = 0 | | XIO_RDSTREAM ( sock1 ) - > eof > = 2 ) {
if ( XIO_RDSTREAM ( sock1 ) - > ignoreeof & & ! closing ) {
Debug1 ( " socket 1 (fd %d) is at EOF, ignoring " ,
XIO_RDSTREAM ( sock1 ) - > fd ) ; /*! */
polling = 1 ;
} else {
Notice1 ( " socket 1 (fd %d) is at EOF " , XIO_GETRDFD ( sock1 ) ) ;
xioshutdown ( sock2 , SHUT_WR ) ;
if ( socat_opts . lefttoright ) {
break ;
}
}
}
if ( bytes2 = = 0 | | XIO_RDSTREAM ( sock2 ) - > eof > = 2 ) {
if ( XIO_RDSTREAM ( sock2 ) - > ignoreeof & & ! closing ) {
Debug1 ( " socket 2 (fd %d) is at EOF, ignoring " ,
XIO_RDSTREAM ( sock2 ) - > fd ) ;
polling = 1 ;
} else {
Notice1 ( " socket 2 (fd %d) is at EOF " , XIO_GETRDFD ( sock2 ) ) ;
xioshutdown ( sock1 , SHUT_WR ) ;
if ( socat_opts . righttoleft ) {
break ;
}
}
}
}
/* close everything that's still open */
xioclose ( sock1 ) ;
xioclose ( sock2 ) ;
return 0 ;
}
# define MAXTIMESTAMPLEN 128
/* prints the timestamp to the buffer and terminates it with '\0'. This buffer
should be at least MAXTIMESTAMPLEN bytes long .
returns 0 on success or - 1 if an error occurred */
int gettimestamp ( char * timestamp ) {
size_t bytes ;
# if HAVE_GETTIMEOFDAY || 1
struct timeval now ;
int result ;
time_t nowt ;
# else /* !HAVE_GETTIMEOFDAY */
time_t now ;
# endif /* !HAVE_GETTIMEOFDAY */
# if HAVE_GETTIMEOFDAY || 1
result = gettimeofday ( & now , NULL ) ;
if ( result < 0 ) {
return result ;
} else {
nowt = now . tv_sec ;
# if HAVE_STRFTIME
bytes = strftime ( timestamp , 20 , " %Y/%m/%d %H:%M:%S " , localtime ( & nowt ) ) ;
bytes + = sprintf ( timestamp + 19 , " . " F_tv_usec " " , now . tv_usec ) ;
# else
strcpy ( timestamp , ctime ( & nowt ) ) ;
bytes = strlen ( timestamp ) ;
# endif
}
# else /* !HAVE_GETTIMEOFDAY */
now = time ( NULL ) ; if ( now = = ( time_t ) - 1 ) {
return - 1 ;
} else {
# if HAVE_STRFTIME
bytes = strftime ( timestamp , 21 , " %Y/%m/%d %H:%M:%S " , localtime ( & now ) ) ;
# else
strcpy ( timestamp , ctime ( & now ) ) ;
bytes = strlen ( timestamp ) ;
# endif
}
# endif /* !HAVE_GETTIMEOFDAY */
return 0 ;
}
static const char * prefixltor = " > " ;
static const char * prefixrtol = " < " ;
static unsigned long numltor ;
static unsigned long numrtol ;
/* print block header (during verbose or hex dump)
returns 0 on success or - 1 if an error occurred */
static int
xioprintblockheader ( FILE * file , size_t bytes , bool righttoleft ) {
char timestamp [ MAXTIMESTAMPLEN ] ;
char buff [ 128 + MAXTIMESTAMPLEN ] ;
if ( gettimestamp ( timestamp ) < 0 ) {
return - 1 ;
}
if ( righttoleft ) {
sprintf ( buff , " %s%s length= " F_Zu " from=%lu to=%lu \n " ,
prefixrtol , timestamp , bytes , numrtol , numrtol + bytes - 1 ) ;
numrtol + = bytes ;
} else {
sprintf ( buff , " %s%s length= " F_Zu " from=%lu to=%lu \n " ,
prefixltor , timestamp , bytes , numltor , numltor + bytes - 1 ) ;
numltor + = bytes ;
}
fputs ( buff , file ) ;
return 0 ;
}
/* inpipe is suspected to have read data available; read at most bufsiz bytes
and transfer them to outpipe . Perform required data conversions .
buff should be at least twice as large as bufsiz , to allow all standard
conversions . Returns the number of bytes written , or 0 on EOF or < 0 if an
error occurred or when data was read but none written due to conversions
( with EAGAIN ) . EAGAIN also occurs when reading from a nonblocking FD where
the file has a mandatory lock .
If 0 bytes were read ( EOF ) , it does NOT shutdown or close a channel , and it
does NOT write a zero bytes block .
*/
/* inpipe, outpipe must be single descriptors (not dual!) */
int xiotransfer ( xiofile_t * inpipe , xiofile_t * outpipe ,
unsigned char * * buff , size_t bufsiz , bool righttoleft ) {
ssize_t bytes , writt ;
bytes = xioread ( inpipe , * buff , socat_opts . bufsiz ) ;
if ( bytes < 0 ) {
if ( errno ! = EAGAIN )
XIO_RDSTREAM ( inpipe ) - > eof = 2 ;
/*xioshutdown(inpipe, SHUT_RD);*/
return - 1 ;
}
if ( bytes = = 0 & & XIO_RDSTREAM ( inpipe ) - > ignoreeof & & ! closing ) {
writt = 0 ;
} else if ( bytes = = 0 ) {
XIO_RDSTREAM ( inpipe ) - > eof = 2 ;
closing = MAX ( closing , 1 ) ;
writt = 0 ;
}
else /* if (bytes > 0)*/ {
if ( XIO_RDSTREAM ( inpipe ) - > lineterm ! =
XIO_WRSTREAM ( outpipe ) - > lineterm ) {
cv_newline ( buff , & bytes ,
XIO_RDSTREAM ( inpipe ) - > lineterm ,
XIO_WRSTREAM ( outpipe ) - > lineterm ) ;
}
if ( bytes = = 0 ) {
errno = EAGAIN ; return - 1 ;
}
if ( socat_opts . verbose & & socat_opts . verbhex ) {
/* Hack-o-rama */
size_t i = 0 ;
size_t j ;
size_t N = 16 ;
const unsigned char * end , * s , * t ;
s = * buff ;
end = ( * buff ) + bytes ;
xioprintblockheader ( stderr , bytes , righttoleft ) ;
while ( s < end ) {
/*! prefix? */
j = Min ( N , ( size_t ) ( end - s ) ) ;
/* print hex */
t = s ;
i = 0 ;
while ( i < j ) {
int c = * t + + ;
fprintf ( stderr , " %02x " , c ) ;
+ + i ;
if ( c = = ' \n ' ) break ;
}
/* fill hex column */
while ( i < N ) {
fputs ( " " , stderr ) ;
+ + i ;
}
fputs ( " " , stderr ) ;
/* print acsii */
t = s ;
i = 0 ;
while ( i < j ) {
int c = * t + + ;
if ( c = = ' \n ' ) {
fputc ( ' . ' , stderr ) ;
break ;
}
if ( ! isprint ( c ) )
c = ' . ' ;
fputc ( c , stderr ) ;
+ + i ;
}
fputc ( ' \n ' , stderr ) ;
s = t ;
}
fputs ( " -- \n " , stderr ) ;
} else if ( socat_opts . verbose ) {
size_t i = 0 ;
xioprintblockheader ( stderr , bytes , righttoleft ) ;
while ( i < ( size_t ) bytes ) {
int c = ( * buff ) [ i ] ;
if ( i > 0 & & ( * buff ) [ i - 1 ] = = ' \n ' )
/*! prefix? */ ;
switch ( c ) {
case ' \a ' : fputs ( " \\ a " , stderr ) ; break ;
case ' \b ' : fputs ( " \\ b " , stderr ) ; break ;
case ' \t ' : fputs ( " \t " , stderr ) ; break ;
case ' \n ' : fputs ( " \n " , stderr ) ; break ;
case ' \v ' : fputs ( " \\ v " , stderr ) ; break ;
case ' \f ' : fputs ( " \\ f " , stderr ) ; break ;
case ' \r ' : fputs ( " \\ r " , stderr ) ; break ;
case ' \\ ' : fputs ( " \\ \\ " , stderr ) ; break ;
default :
if ( ! isprint ( c ) )
c = ' . ' ;
fputc ( c , stderr ) ;
break ;
}
+ + i ;
}
} else if ( socat_opts . verbhex ) {
int i ;
/*! prefix? */
for ( i = 0 ; i < bytes ; + + i ) {
fprintf ( stderr , " %02x " , ( * buff ) [ i ] ) ;
}
fputc ( ' \n ' , stderr ) ;
}
writt = xiowrite ( outpipe , * buff , bytes ) ;
if ( writt < 0 ) {
/* EAGAIN when nonblocking but a mandatory lock is on file.
the problem with EAGAIN is that the read cannot be repeated ,
so we need to buffer the data and try to write it later
again . not yet implemented , sorry . */
#if 0
if ( errno = = EPIPE ) {
return 0 ; /* can no longer write; handle like EOF */
}
# endif
return - 1 ;
} else {
Info3 ( " transferred " F_Zu " bytes from %d to %d " ,
writt , XIO_GETRDFD ( inpipe ) , XIO_GETWRFD ( outpipe ) ) ;
}
}
return writt ;
}
# define CR '\r'
# define LF '\n'
int cv_newline ( unsigned char * * buff , ssize_t * bytes ,
int lineterm1 , int lineterm2 ) {
/* must perform newline changes */
if ( lineterm1 < = LINETERM_CR & & lineterm2 < = LINETERM_CR ) {
/* no change in data length */
unsigned char from , to , * p , * z ;
if ( lineterm1 = = LINETERM_RAW ) {
from = ' \n ' ; to = ' \r ' ;
} else {
from = ' \r ' ; to = ' \n ' ;
}
z = * buff + * bytes ;
p = * buff ;
while ( p < z ) {
if ( * p = = from ) * p = to ;
+ + p ;
}
} else if ( lineterm1 = = LINETERM_CRNL ) {
/* buffer becomes shorter */
unsigned char to , * s , * t , * z ;
if ( lineterm2 = = LINETERM_RAW ) {
to = ' \n ' ;
} else {
to = ' \r ' ;
}
z = * buff + * bytes ;
s = t = * buff ;
while ( s < z ) {
if ( * s = = ' \r ' ) {
+ + s ;
continue ;
}
if ( * s = = ' \n ' ) {
* t + + = to ; + + s ;
} else {
* t + + = * s + + ;
}
}
* bytes = t - * buff ;
} else {
/* buffer becomes longer, must alloc another space */
unsigned char * buf2 ;
unsigned char from ; unsigned char * s , * t , * z ;
if ( lineterm1 = = LINETERM_RAW ) {
from = ' \n ' ;
} else {
from = ' \r ' ;
}
if ( ( buf2 = Malloc ( 2 * socat_opts . bufsiz + 1 ) ) = = NULL ) {
return - 1 ;
}
s = * buff ; t = buf2 ; z = * buff + * bytes ;
while ( s < z ) {
if ( * s = = from ) {
* t + + = ' \r ' ; * t + + = ' \n ' ;
+ + s ;
continue ;
} else {
* t + + = * s + + ;
}
}
free ( * buff ) ;
* buff = buf2 ;
* bytes = t - buf2 ; ;
}
return 0 ;
}
void socat_signal ( int signum ) {
switch ( signum ) {
case SIGQUIT :
case SIGILL :
case SIGABRT :
case SIGBUS :
case SIGFPE :
case SIGSEGV :
case SIGPIPE :
diag_set_int ( ' x ' , 128 + signum ) ; /* in case Error exits for us */
Error1 ( " exiting on signal %d " , signum ) ;
diag_set_int ( ' x ' , 0 ) ; /* in case Error did not exit */
break ;
case SIGTERM :
Warn1 ( " exiting on signal %d " , signum ) ; break ;
case SIGHUP :
case SIGINT :
Notice1 ( " exiting on signal %d " , signum ) ; break ;
}
Exit ( 128 + signum ) ;
}
/* this is the callback when the child of an address died */
static int socat_sigchild ( struct single * file ) {
if ( file - > ignoreeof & & ! closing ) {
;
} else {
file - > eof = MAX ( file - > eof , 1 ) ;
closing = 1 ;
}
return 0 ;
}
static int socat_lock ( void ) {
int lockrc ;
# if 1
if ( ( lockrc = xiolock ( & socat_opts . lock ) ) < 0 ) {
return - 1 ;
}
if ( lockrc = = 0 ) {
havelock = true ;
}
return lockrc ;
# else
if ( socat_opts . lock . lockfile ) {
if ( ( lockrc = xiolock ( socat_opts . lock . lockfile ) ) < 0 ) {
/*Error1("error with lockfile \"%s\"", socat_opts.lock.lockfile);*/
return - 1 ;
}
if ( lockrc ) {
return 1 ;
}
havelock = true ;
/*0 Info1("obtained lock \"%s\"", socat_opts.lock.lockfile);*/
}
if ( socat_opts . lock . waitlock ) {
if ( xiowaitlock ( socat_opts . lock . waitlock , socat_opts . lock . intervall ) ) {
/*Error1("error with lockfile \"%s\"", socat_opts.lock.lockfile);*/
return - 1 ;
} else {
havelock = true ;
/*0 Info1("obtained lock \"%s\"", socat_opts.lock.waitlock);*/
}
}
return 0 ;
# endif
}
static void socat_unlock ( void ) {
if ( ! havelock ) return ;
if ( socat_opts . lock . lockfile ) {
if ( Unlink ( socat_opts . lock . lockfile ) < 0 ) {
Warn2 ( " unlink( \" %s \" ): %s " ,
socat_opts . lock . lockfile , strerror ( errno ) ) ;
} else {
Info1 ( " released lock \" %s \" " , socat_opts . lock . lockfile ) ;
}
}
}
/* this is a callback function that may be called by the newchild hook of xio
*/
static int socat_newchild ( void ) {
havelock = false ;
return 0 ;
}