2008-01-28 21:37:16 +00:00
/* source: socat.c */
2016-07-22 06:54:31 +00:00
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
2008-01-27 12:00:08 +00: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 */
2023-06-17 13:31:26 +00:00
unsigned long log_sigs ; /* signals to be caught just for logging */
2023-10-26 16:42:41 +00:00
bool statistics ; /* log statistics on exit */
2008-01-27 12:00:08 +00:00
} 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 */
2023-06-17 13:31:26 +00:00
1 < < SIGHUP | 1 < < SIGINT | 1 < < SIGQUIT | 1 < < SIGILL | 1 < < SIGABRT | 1 < < SIGBUS | 1 < < SIGFPE | 1 < < SIGSEGV | 1 < < SIGTERM , /* log_sigs */
2023-10-26 16:42:41 +00:00
false /* statistics */
2008-01-27 12:00:08 +00:00
} ;
void socat_usage ( FILE * fd ) ;
2019-03-03 14:08:02 +00:00
void socat_opt_hint ( FILE * fd , char a , char b ) ;
2008-01-27 12:00:08 +00:00
void socat_version ( FILE * fd ) ;
int socat ( const char * address1 , const char * address2 ) ;
int _socat ( void ) ;
2020-12-29 04:07:03 +00:00
int cv_newline ( unsigned char * buff , ssize_t * bytes , int lineterm1 , int lineterm2 ) ;
2008-01-27 12:00:08 +00:00
void socat_signal ( int sig ) ;
2023-10-26 16:42:41 +00:00
void socat_signal_logstats ( int sig ) ;
2008-01-27 12:00:08 +00:00
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 ) ;
2023-10-26 16:42:41 +00:00
static void socat_print_stats ( void ) ;
2008-01-27 12:00:08 +00:00
static const char socatversion [ ] =
# include "./VERSION"
;
2015-01-04 19:22:13 +00:00
static const char timestamp [ ] = BUILD_DATE ;
2008-01-27 12:00:08 +00:00
2016-07-22 06:54:31 +00:00
const char copyright_socat [ ] = " socat by Gerhard Rieger and contributors - see www.dest-unreach.org " ;
2008-01-27 12:00:08 +00:00
# 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 19:51:38 +00:00
char * mainwaitstring ;
2008-01-27 12:00:08 +00:00
char buff [ 10 ] ;
double rto ;
int i , argc0 , result ;
2021-10-26 18:41:08 +00:00
bool isdash = false ;
2023-06-14 09:14:46 +00:00
int msglevel = 0 ;
2008-01-27 12:00:08 +00:00
struct utsname ubuf ;
int lockrc ;
2008-07-24 19:51:38 +00:00
if ( mainwaitstring = getenv ( " SOCAT_MAIN_WAIT " ) ) {
sleep ( atoi ( mainwaitstring ) ) ;
}
2008-01-27 12:00:08 +00: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 ] ) {
2019-03-03 14:08:02 +00:00
case ' V ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_usage ( stderr ) ; Exit ( 1 ) ; }
socat_version ( stdout ) ; Exit ( 0 ) ;
2008-01-27 12:00:08 +00:00
# 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 */
2019-03-03 14:08:02 +00:00
case ' d ' :
2023-06-14 09:14:46 +00:00
a = * arg1 + 2 ;
switch ( * a ) {
case ' d ' :
break ;
case ' - ' : case ' 0 ' : case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' :
{
char * endptr ;
msglevel = strtol ( a , & endptr , 0 ) ;
if ( endptr = = a | | * endptr ) {
Error2 ( " Invalid (trailing) character(s) \" %c \" in \" %s \" option " , * a , * arg1 ) ;
}
diag_set_int ( ' d ' , 4 - msglevel ) ;
}
break ;
case ' \0 ' :
+ + msglevel ;
diag_set_int ( ' d ' , 4 - msglevel ) ;
break ;
default : socat_usage ( stderr ) ;
}
if ( * a ! = ' d ' ) break ;
+ + msglevel ;
2019-03-03 14:08:02 +00:00
while ( * a ) {
if ( * a = = ' d ' ) {
2023-06-14 09:14:46 +00:00
+ + msglevel ;
diag_set_int ( ' d ' , 4 - msglevel ) ;
2019-03-03 14:08:02 +00:00
} else {
socat_usage ( stderr ) ;
Exit ( 1 ) ;
}
+ + a ;
}
break ;
2008-01-27 12:00:08 +00:00
# if WITH_FILAN
2019-03-03 14:08:02 +00:00
case ' D ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
socat_opts . debug = true ; break ;
2008-01-27 12:00:08 +00:00
# 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 ;
2019-03-03 14:08:02 +00:00
case ' v ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
socat_opts . verbose = true ; break ;
case ' x ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
socat_opts . verbhex = true ; break ;
2020-12-31 12:19:19 +00:00
case ' r ' : if ( arg1 [ 0 ] [ 2 ] ) {
a = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( a = * arg1 ) = = NULL ) {
Error ( " option -r requires an argument; use option \" -h \" for help " ) ;
break ;
}
}
2023-10-26 14:57:39 +00:00
xiosetopt ( ' r ' , a ) ;
2020-12-31 12:19:19 +00:00
break ;
case ' R ' : if ( arg1 [ 0 ] [ 2 ] ) {
a = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( a = * arg1 ) = = NULL ) {
Error ( " option -R requires an argument; use option \" -h \" for help " ) ;
break ;
}
}
2023-10-26 14:57:39 +00:00
xiosetopt ( ' R ' , a ) ;
2020-12-31 12:19:19 +00:00
break ;
2008-01-27 12:00:08 +00:00
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 ) ;
}
}
2022-08-12 11:19:03 +00:00
socat_opts . bufsiz = Strtoul ( a , ( char * * ) & a , 0 , " -b " ) ;
2008-01-27 12:00:08 +00:00
break ;
2019-03-03 14:08:02 +00:00
case ' s ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
2008-01-27 12:00:08 +00:00
diag_set_int ( ' e ' , E_FATAL ) ; break ;
2023-06-17 13:31:26 +00:00
case ' S ' : /* do not catch signals */
if ( arg1 [ 0 ] [ 2 ] ) {
a = * arg1 + 2 ;
} else {
+ + arg1 , - - argc ;
if ( ( a = * arg1 ) = = NULL ) {
Error ( " option -S requires an argument; use option \" -h \" for help " ) ;
Exit ( 1 ) ;
}
}
socat_opts . log_sigs = Strtoul ( a , ( char * * ) & a , 0 , " -S " ) ;
break ;
2008-01-27 12:00:08 +00:00
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 ) ;
}
}
2022-08-12 11:19:03 +00:00
rto = Strtod ( a , ( char * * ) & a , " -t " ) ;
2008-01-27 12:00:08 +00:00
socat_opts . closwait . tv_sec = rto ;
socat_opts . closwait . tv_usec =
2023-06-12 21:01:54 +00:00
( rto - socat_opts . closwait . tv_sec ) * 1000000 ;
2008-01-27 12:00:08 +00:00
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 ) ;
}
}
2022-08-12 11:19:03 +00:00
rto = Strtod ( a , ( char * * ) & a , " -T " ) ;
2008-01-27 12:00:08 +00:00
socat_opts . total_timeout . tv_sec = rto ;
socat_opts . total_timeout . tv_usec =
2023-06-12 21:01:54 +00:00
( rto - socat_opts . total_timeout . tv_sec ) * 1000000 ;
2008-01-27 12:00:08 +00:00
break ;
2019-03-03 14:08:02 +00:00
case ' u ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
socat_opts . lefttoright = true ; break ;
case ' U ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
socat_opts . righttoleft = true ; break ;
case ' g ' : if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
xioopts_ignoregroups = true ; break ;
2008-01-27 12:00:08 +00:00
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 ;
2023-09-30 07:26:13 +00:00
case ' W ' : if ( socat_opts . lock . lockfile ) {
2008-01-27 12:00:08 +00:00
Error ( " only one -L and -W option allowed " ) ;
2023-09-30 07:26:13 +00:00
}
2008-01-27 12:00:08 +00:00
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
2019-03-03 14:08:02 +00:00
if ( arg1 [ 0 ] [ 2 ] ) { socat_opt_hint ( stderr , arg1 [ 0 ] [ 1 ] , arg1 [ 0 ] [ 2 ] ) ; Exit ( 1 ) ; }
2023-06-23 14:21:05 +00:00
xioparms . default_ip = arg1 [ 0 ] [ 1 ] ;
xioparms . preferred_ip = arg1 [ 0 ] [ 1 ] ;
2008-01-27 12:00:08 +00:00
break ;
# endif /* WITH_IP4 || WITH_IP6 */
2023-09-30 07:26:13 +00:00
case ' - ' :
if ( ! strcmp ( " experimental " , & arg1 [ 0 ] [ 2 ] ) ) {
2023-06-23 14:21:05 +00:00
xioparms . experimental = true ;
2023-10-26 16:42:41 +00:00
} else if ( ! strcmp ( " statistics " , & arg1 [ 0 ] [ 2 ] ) ) {
socat_opts . statistics = true ;
2023-09-30 07:26:13 +00:00
} else {
Error1 ( " unknown option \" %s \" ; use option \" -h \" for help " , arg1 [ 0 ] ) ;
}
break ;
2008-01-27 12:00:08 +00:00
case ' \0 ' :
case ' , ' :
2021-10-26 18:41:08 +00:00
case ' : ' :
isdash = true ;
break ; /* this "-" is a variation of STDIO */
2008-01-27 12:00:08 +00:00
default :
2021-10-26 18:41:08 +00:00
xioinqopt ( ' p ' , buff , sizeof ( buff ) ) ; /* fetch pipe separator char */
2008-01-27 12:00:08 +00:00
if ( arg1 [ 0 ] [ 1 ] = = buff [ 0 ] ) {
2021-10-26 18:41:08 +00:00
isdash = true ;
2008-01-27 12:00:08 +00:00
break ;
}
Error1 ( " unknown option \" %s \" ; use option \" -h \" for help " , arg1 [ 0 ] ) ;
Exit ( 1 ) ;
}
2021-10-26 18:41:08 +00:00
if ( isdash ) {
/* the leading "-" is a form of the first address */
2008-01-27 12:00:08 +00:00
break ;
2021-10-26 18:41:08 +00:00
}
2008-01-27 12:00:08 +00:00
+ + 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 " ) ;
}
2008-09-22 20:17:55 +00:00
xioinitialize2 ( ) ;
2008-01-27 12:00:08 +00:00
Info ( copyright_socat ) ;
# if WITH_OPENSSL
Info ( copyright_openssl ) ;
Info ( copyright_ssleay ) ;
# endif
Debug2 ( " socat version %s on %s " , socatversion , timestamp ) ;
2015-01-12 22:34:47 +00:00
xiosetenv ( " VERSION " , socatversion , 1 , NULL ) ; /* SOCAT_VERSION */
2008-01-27 12:00:08 +00:00
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 */
2015-01-12 20:46:16 +00:00
{
2023-10-26 16:42:41 +00:00
# if HAVE_SIGACTION
2015-01-12 20:46:16 +00:00
struct sigaction act ;
2023-10-26 16:42:41 +00:00
# endif
2023-06-17 13:31:26 +00:00
int i , m ;
sigfillset ( & act . sa_mask ) ; /* while in sighandler block all signals */
2015-01-12 20:46:16 +00:00
act . sa_flags = 0 ;
act . sa_handler = socat_signal ;
2017-01-08 10:50:11 +00:00
/* not sure which signals should be caught and print a message */
2023-06-17 13:31:26 +00:00
for ( i = 0 , m = 1 ; i < 8 * sizeof ( unsigned long ) ; + + i , m < < = 1 ) {
if ( socat_opts . log_sigs & m ) {
2023-10-26 16:42:41 +00:00
# if HAVE_SIGACTION
2023-06-17 13:31:26 +00:00
Sigaction ( i , & act , NULL ) ;
2023-10-26 16:42:41 +00:00
# else
Signal ( i , socat_signal ) ;
# endif
2023-06-17 13:31:26 +00:00
}
}
2023-10-26 16:42:41 +00:00
# if HAVE_SIGACTION
act . sa_handler = socat_signal_logstats ;
Sigaction ( SIGUSR1 , & act , NULL ) ;
# else
Signal ( SIGUSR1 , socat_signal_logstats ) ;
# endif
2015-01-12 20:46:16 +00:00
}
Signal ( SIGPIPE , SIG_IGN ) ;
2008-01-27 12:00:08 +00:00
/* 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 ) ;
2023-10-26 16:42:41 +00:00
if ( socat_opts . statistics ) {
Atexit ( socat_print_stats ) ;
}
2008-01-27 12:00:08 +00:00
result = socat ( arg1 [ 0 ] , arg1 [ 1 ] ) ;
2023-06-15 10:53:32 +00:00
if ( result = = EXIT_SUCCESS & & engine_result ! = EXIT_SUCCESS ) {
result = engine_result ; /* a signal handler reports failure */
}
2008-01-27 12:00:08 +00:00
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 ) ;
2023-10-26 12:56:50 +00:00
fputs ( " options (general command line options): \n " , fd ) ;
2008-01-27 12:00:08 +00:00
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 */
2023-06-23 14:21:05 +00:00
fputs ( " -d[ddd] increase verbosity (use up to 4 times; 2 are recommended) \n " , fd ) ;
fputs ( " -d0|1|2|3|4 set verbosity level (0: Errors; 4 all including Debug) \n " , fd ) ;
2008-01-27 12:00:08 +00:00
# if WITH_FILAN
fputs ( " -D analyze file descriptors before loop \n " , fd ) ;
# endif
2023-09-30 07:26:13 +00:00
fputs ( " --experimental enable experimental features \n " , fd ) ;
2023-10-26 16:42:41 +00:00
fputs ( " --statistics output transfer statistics on exit \n " , fd ) ;
2008-01-27 12:00:08 +00:00
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 ) ;
2023-06-17 13:31:26 +00:00
fputs ( " -lp<progname> set the program name used for logging and vars \n " , fd ) ;
2008-01-27 12:00:08 +00:00
fputs ( " -lu use microseconds for logging timestamps \n " , fd ) ;
fputs ( " -lh add hostname to log messages \n " , fd ) ;
2020-12-31 12:19:19 +00:00
fputs ( " -v verbose text dump of data traffic \n " , fd ) ;
fputs ( " -x verbose hexadecimal dump of data traffic \n " , fd ) ;
fputs ( " -r <file> raw dump of data flowing from left to right \n " , fd ) ;
fputs ( " -R <file> raw dump of data flowing from right to left \n " , fd ) ;
2008-01-27 12:00:08 +00:00
fputs ( " -b<size_t> set data buffer size (8192) \n " , fd ) ;
fputs ( " -s sloppy (continue on error) \n " , fd ) ;
2023-06-17 13:31:26 +00:00
fputs ( " -S<sigmask> log these signals, override default \n " , fd ) ;
2008-01-27 12:00:08 +00:00
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
}
2019-03-03 14:08:02 +00:00
void socat_opt_hint ( FILE * fd , char a , char b ) {
fprintf ( fd , " Do not merge single character options, i.e. use \" -%c -%c \" instead of \" -%c%c \" \n " ,
a , b , a , b ) ;
}
2008-01-27 12:00:08 +00:00
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 ) ;
2023-10-26 16:42:41 +00:00
# ifdef WITH_HELP
fprintf ( fd , " #define WITH_HELP %d \n " , WITH_HELP ) ;
# else
fputs ( " #undef WITH_HELP \n " , fd ) ;
# endif
# ifdef WITH_STATS
fprintf ( fd , " #define WITH_STATS %d \n " , WITH_STATS ) ;
# else
fputs ( " #undef WITH_STATS \n " , fd ) ;
# endif
2008-01-27 12:00:08 +00:00
# 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
2008-09-24 14:15:11 +00:00
# ifdef WITH_GENERICSOCKET
fprintf ( fd , " #define WITH_GENERICSOCKET %d \n " , WITH_GENERICSOCKET ) ;
# else
fputs ( " #undef WITH_GENERICSOCKET \n " , fd ) ;
# endif
2008-09-20 21:37:56 +00:00
# ifdef WITH_INTERFACE
fprintf ( fd , " #define WITH_INTERFACE %d \n " , WITH_INTERFACE ) ;
# else
fputs ( " #undef WITH_INTERFACE \n " , fd ) ;
# endif
2008-01-27 12:00:08 +00:00
# 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
2008-09-22 21:21:26 +00:00
# ifdef WITH_SCTP
fprintf ( fd , " #define WITH_SCTP %d \n " , WITH_SCTP ) ;
# else
fputs ( " #undef WITH_SCTP \n " , fd ) ;
# endif
2008-01-27 12:00:08 +00:00
# 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
2020-12-26 21:46:36 +00:00
# ifdef WITH_VSOCK
fprintf ( fd , " #define WITH_VSOCK %d \n " , WITH_VSOCK ) ;
# else
fputs ( " #undef WITH_VSOCK \n " , fd ) ;
# endif
2023-07-21 05:10:38 +00:00
# ifdef WITH_NAMESPACES
fprintf ( fd , " #define WITH_NAMESPACES %d \n " , WITH_NAMESPACES ) ;
# else
fputs ( " #undef WITH_NAMESPACES \n " , fd ) ;
# endif
2008-01-27 12:00:08 +00:00
# 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
2023-10-26 20:16:21 +00:00
# ifdef WITH_DEFAULT_IPV
# if WITH_DEFAULT_IPV
fprintf ( fd , " #define WITH_DEFAULT_IPV %c \n " , WITH_DEFAULT_IPV ) ;
# else
fprintf ( fd , " #define WITH_DEFAULT_IPV ' \\ 0' \n " ) ;
# endif
# else
fputs ( " #undef WITH_DEFAULT_IPV \n " , fd ) ;
# endif
2008-01-27 12:00:08 +00:00
}
xiofile_t * sock1 , * sock2 ;
int closing = 0 ; /* 0..no eof yet, 1..first eof just occurred,
2. . counting down closing timeout */
2023-10-26 14:57:39 +00:00
int sniffleft = - 1 ; /* -1 or an FD for teeing data arriving on xfd1 */
int sniffright = - 1 ; /* -1 or an FD for teeing data arriving on xfd2 */
2008-01-27 12:00:08 +00:00
/* 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 ( 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 ) ) {
2020-12-28 10:10:03 +00:00
int i ;
for ( i = 0 ; i < NUMUNKNOWN ; + + i ) {
if ( XIO_RDSTREAM ( sock1 ) - > para . exec . pid = = diedunknown [ i ] ) {
/* child has alread died... but it might have put regular data into
the communication channel , so continue */
Info2 ( " child " F_pid " has already died with status %d " ,
XIO_RDSTREAM ( sock1 ) - > para . exec . pid , statunknown [ i ] ) ;
2023-06-23 20:48:26 +00:00
+ + num_child ; /* it was counted as anonymous child, undo */
2020-12-28 10:10:03 +00:00
if ( statunknown [ i ] ! = 0 ) {
return 1 ;
}
diedunknown [ i ] = 0 ;
XIO_RDSTREAM ( sock1 ) - > para . exec . pid = 0 ;
/* return STAT_RETRYLATER; */
}
2008-01-27 12:00:08 +00:00
}
}
# 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 ) ) {
2020-12-28 10:10:03 +00:00
int i ;
for ( i = 0 ; i < NUMUNKNOWN ; + + i ) {
if ( XIO_RDSTREAM ( sock2 ) - > para . exec . pid = = diedunknown [ i ] ) {
/* child has alread died... but it might have put regular data into
the communication channel , so continue */
Info2 ( " child " F_pid " has already died with status %d " ,
XIO_RDSTREAM ( sock2 ) - > para . exec . pid , statunknown [ i ] ) ;
if ( statunknown [ i ] ! = 0 ) {
return 1 ;
}
diedunknown [ i ] = 0 ;
XIO_RDSTREAM ( sock2 ) - > para . exec . pid = 0 ;
/* return STAT_RETRYLATER; */
}
2008-01-27 12:00:08 +00:00
}
}
# endif
Info ( " resolved and opened all sock addresses " ) ;
2023-06-12 21:01:54 +00:00
return _socat ( ) ; /* nsocks, sockets are visible outside function */
2008-01-27 12:00:08 +00:00
}
/* 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 19:51:38 +00:00
struct pollfd in ;
2008-01-27 12:00:08 +00:00
int retval ;
2008-07-24 19:51:38 +00:00
2008-01-27 12:00:08 +00: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 ) {
2008-09-29 19:18:58 +00:00
struct timeval timeout = { 0 , 0 } ;
2008-01-27 12:00:08 +00:00
if ( XIO_READABLE ( xfd ) & & ! ( XIO_RDSTREAM ( xfd ) - > eof > = 2 & & ! XIO_RDSTREAM ( xfd ) - > ignoreeof ) ) {
2008-07-24 19:51:38 +00:00
in . fd = XIO_GETRDFD ( xfd ) ;
in . events = POLLIN /*|POLLRDBAND*/ ;
in . revents = 0 ;
2008-01-27 12:00:08 +00:00
}
do {
2015-01-12 20:46:16 +00:00
int _errno ;
2008-09-29 19:18:58 +00:00
retval = xiopoll ( & in , 1 , & timeout ) ;
2015-01-12 20:46:16 +00:00
_errno = errno ; diag_flush ( ) ; errno = _errno ; /* just in case it's not debug level and Msg() not been called */
2008-01-27 12:00:08 +00:00
} while ( retval < 0 & & errno = = EINTR ) ;
if ( retval < 0 ) {
2008-10-14 20:15:18 +00:00
Error5 ( " xiopoll({%d,%0o}, 1, { " F_tv_sec " . " F_tv_usec " }): %s " ,
in . fd , in . events , timeout . tv_sec , timeout . tv_usec ,
strerror ( errno ) ) ;
2008-01-27 12:00:08 +00:00
return - 1 ;
2008-07-24 19:51:38 +00:00
}
if ( retval = = 0 ) {
2008-01-27 12:00:08 +00: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 ,
2020-12-29 04:07:03 +00:00
unsigned char * buff , size_t bufsiz , bool righttoleft ) ;
2008-01-27 12:00:08 +00:00
2008-09-04 21:42:52 +00:00
bool mayrd1 ; /* sock1 has read data or eof, according to poll() */
bool mayrd2 ; /* sock2 has read data or eof, according to poll() */
bool maywr1 ; /* sock1 can be written to, according to poll() */
bool maywr2 ; /* sock2 can be written to, according to poll() */
2008-01-27 12:00:08 +00:00
/* 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 ) {
2023-06-11 20:04:25 +00:00
char * transferwaitstring ;
2008-07-24 19:51:38 +00:00
struct pollfd fds [ 4 ] ,
* fd1in = & fds [ 0 ] ,
* fd1out = & fds [ 1 ] ,
* fd2in = & fds [ 2 ] ,
* fd2out = & fds [ 3 ] ;
2008-01-27 12:00:08 +00:00
int retval ;
unsigned char * buff ;
ssize_t bytes1 , bytes2 ;
int polling = 0 ; /* handling ignoreeof */
2008-09-04 21:42:52 +00:00
int wasaction = 1 ; /* last poll was active, do NOT sleep before next */
2008-01-27 12:00:08 +00:00
struct timeval total_timeout ; /* the actual total timeout timer */
2023-10-26 14:57:39 +00:00
{
/* Open sniff file(s) */
char name [ PATH_MAX ] ;
struct timeval tv = { 0 } ; /* 'cache' to have same time in both */
if ( xioinqopt ( ' r ' , name , sizeof ( name ) ) = = 0 ) {
if ( sniffleft > = 0 ) Close ( sniffleft ) ;
sniffleft = xio_opensnifffile ( name , & tv ) ;
if ( sniffleft < 0 ) {
Error2 ( " option -r \" %s \" : %s " , name , strerror ( errno ) ) ;
}
}
if ( xioinqopt ( ' R ' , name , sizeof ( name ) ) = = 0 ) {
if ( sniffright > = 0 ) Close ( sniffright ) ;
sniffright = xio_opensnifffile ( name , & tv ) ;
if ( sniffright < 0 ) {
Error2 ( " option -R \" %s \" : %s " , name , strerror ( errno ) ) ;
}
}
}
2008-01-27 12:00:08 +00:00
# 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 */
2020-10-13 18:08:04 +00:00
if ( socat_opts . bufsiz > ( SIZE_MAX - 1 ) / 2 ) {
Error2 ( " buffer size option (-b) to big - " F_Zu " (max is " F_Zu " ) " , socat_opts . bufsiz , ( SIZE_MAX - 1 ) / 2 ) ;
socat_opts . bufsiz = ( SIZE_MAX - 1 ) / 2 ;
}
2020-12-29 04:07:03 +00:00
# if HAVE_PROTOTYPE_LIB_posix_memalign
/* Operations on files with flag O_DIRECT might need buffer alignment.
Without this , eg . read ( ) fails with " Invalid argument " */
{
int _errno ;
if ( ( _errno = Posix_memalign ( ( void * * ) & buff , getpagesize ( ) , 2 * socat_opts . bufsiz + 1 ) ) ! = 0 ) {
Error1 ( " posix_memalign(): %s " , strerror ( _errno ) ) ;
return - 1 ;
}
}
# else /* !HAVE_PROTOTYPE_LIB_posix_memalign */
2008-01-27 12:00:08 +00:00
buff = Malloc ( 2 * socat_opts . bufsiz + 1 ) ;
if ( buff = = NULL ) return - 1 ;
2020-12-29 04:07:03 +00:00
# endif /* !HAVE_PROTOTYPE_LIB_posix_memalign */
2008-01-27 12:00:08 +00:00
if ( socat_opts . logopt = = ' m ' & & xioinqopt ( ' l ' , NULL , 0 ) = = ' m ' ) {
Info ( " switching to syslog " ) ;
2023-06-23 14:21:05 +00:00
diag_set ( ' y ' , xioparms . syslogfac ) ;
2008-01-27 12:00:08 +00:00
xiosetopt ( ' l ' , " \0 " ) ;
}
total_timeout = socat_opts . total_timeout ;
2023-06-11 20:04:25 +00:00
if ( transferwaitstring = getenv ( " SOCAT_TRANSFER_WAIT " ) ) {
Info1 ( " before starting data transfer loop: sleeping %ds (env:SOCAT_TRANSFER_WAIT) " , atoi ( transferwaitstring ) ) ;
sleep ( atoi ( transferwaitstring ) ) ;
}
2008-01-27 12:00:08 +00:00
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-09-29 19:18:58 +00:00
struct timeval timeout , * to = NULL ;
2008-01-27 12:00:08 +00: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 ) {
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 " ) ;
2015-01-06 12:07:15 +00:00
free ( buff ) ;
2008-01-27 12:00:08 +00:00
return 0 ;
}
}
} else {
wasaction = 0 ;
}
}
if ( polling ) {
/* there is a ignoreeof poll timeout, use it */
2008-09-29 19:18:58 +00:00
timeout = socat_opts . pollintv ;
to = & timeout ;
2008-01-27 12:00:08 +00: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-09-29 19:18:58 +00:00
timeout = socat_opts . total_timeout ;
to = & timeout ;
2008-01-27 12:00:08 +00:00
} else {
2008-09-29 19:18:58 +00:00
to = NULL ;
2008-01-27 12:00:08 +00:00
}
if ( closing > = 1 ) {
/* first eof already occurred, start end timer */
2008-09-29 19:18:58 +00:00
timeout = socat_opts . pollintv ;
to = & timeout ;
2008-01-27 12:00:08 +00:00
closing = 2 ;
}
2008-09-19 07:03:59 +00:00
/* frame 1: set the poll parameters and loop over poll() EINTR) */
2008-09-04 21:42:52 +00:00
do { /* loop over poll() EINTR */
2008-01-27 12:00:08 +00:00
int _errno ;
childleftdata ( sock1 ) ;
childleftdata ( sock2 ) ;
if ( closing > = 1 ) {
/* first eof already occurred, start end timer */
2008-09-29 19:18:58 +00:00
timeout = socat_opts . closwait ;
to = & timeout ;
2008-01-27 12:00:08 +00:00
closing = 2 ;
}
2008-09-19 07:03:59 +00:00
/* use the ignoreeof timeout if appropriate */
if ( polling ) {
2008-09-29 19:18:58 +00:00
if ( closing = = 0 | |
( socat_opts . pollintv . tv_sec < timeout . tv_sec ) | |
( ( socat_opts . pollintv . tv_sec = = timeout . tv_sec ) & &
socat_opts . pollintv . tv_usec < timeout . tv_usec ) ) {
timeout = socat_opts . pollintv ;
2008-09-19 07:03:59 +00:00
}
}
/* now the fds will be assigned */
2008-01-27 12:00:08 +00:00
if ( XIO_READABLE ( sock1 ) & &
! ( XIO_RDSTREAM ( sock1 ) - > eof > 1 & & ! XIO_RDSTREAM ( sock1 ) - > ignoreeof ) & &
! socat_opts . righttoleft ) {
2008-09-19 07:03:59 +00:00
if ( ! mayrd1 & & ! ( XIO_RDSTREAM ( sock1 ) - > eof > 1 ) ) {
2008-07-24 19:51:38 +00:00
fd1in - > fd = XIO_GETRDFD ( sock1 ) ;
fd1in - > events = POLLIN ;
} else {
fd1in - > fd = - 1 ;
2008-01-27 12:00:08 +00:00
}
if ( ! maywr2 ) {
2008-07-24 19:51:38 +00:00
fd2out - > fd = XIO_GETWRFD ( sock2 ) ;
fd2out - > events = POLLOUT ;
} else {
fd2out - > fd = - 1 ;
2008-01-27 12:00:08 +00:00
}
2008-07-24 19:51:38 +00:00
} else {
fd1in - > fd = - 1 ;
fd2out - > fd = - 1 ;
2008-01-27 12:00:08 +00:00
}
if ( XIO_READABLE ( sock2 ) & &
! ( XIO_RDSTREAM ( sock2 ) - > eof > 1 & & ! XIO_RDSTREAM ( sock2 ) - > ignoreeof ) & &
! socat_opts . lefttoright ) {
2008-09-19 07:03:59 +00:00
if ( ! mayrd2 & & ! ( XIO_RDSTREAM ( sock2 ) - > eof > 1 ) ) {
2008-07-24 19:51:38 +00:00
fd2in - > fd = XIO_GETRDFD ( sock2 ) ;
fd2in - > events = POLLIN ;
} else {
fd2in - > fd = - 1 ;
2008-01-27 12:00:08 +00:00
}
if ( ! maywr1 ) {
2008-07-24 19:51:38 +00:00
fd1out - > fd = XIO_GETWRFD ( sock1 ) ;
fd1out - > events = POLLOUT ;
} else {
fd1out - > fd = - 1 ;
2008-01-27 12:00:08 +00:00
}
2008-07-24 19:51:38 +00:00
} else {
fd1out - > fd = - 1 ;
fd2in - > fd = - 1 ;
2008-01-27 12:00:08 +00:00
}
2008-09-19 07:03:59 +00:00
/* frame 0: innermost part of the transfer loop: check FD status */
2008-09-29 19:18:58 +00:00
retval = xiopoll ( fds , 4 , to ) ;
2008-09-19 07:03:59 +00:00
if ( retval > = 0 | | errno ! = EINTR ) {
break ;
2008-01-27 12:00:08 +00:00
}
2008-09-19 07:03:59 +00:00
_errno = errno ;
Info1 ( " poll(): %s " , strerror ( errno ) ) ;
2008-01-27 12:00:08 +00:00
errno = _errno ;
2008-09-19 07:03:59 +00:00
} while ( true ) ;
2008-01-27 12:00:08 +00:00
/* 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-10-14 20:15:18 +00:00
Error11 ( " xiopoll({%d,%0o}{%d,%0o}{%d,%0o}{%d,%0o}, 4, { " F_tv_sec " . " F_tv_usec " }): %s " ,
2008-07-24 19:51:38 +00:00
fds [ 0 ] . fd , fds [ 0 ] . events , fds [ 1 ] . fd , fds [ 1 ] . events ,
fds [ 2 ] . fd , fds [ 2 ] . events , fds [ 3 ] . fd , fds [ 3 ] . events ,
2008-10-14 20:15:18 +00:00
timeout . tv_sec , timeout . tv_usec , strerror ( errno ) ) ;
2015-01-06 12:07:15 +00:00
free ( buff ) ;
2008-01-27 12:00:08 +00:00
return - 1 ;
} else if ( retval = = 0 ) {
2008-09-04 21:42:52 +00:00
Info2 ( " poll timed out (no data within %ld.%06ld seconds) " ,
2008-01-27 12:00:08 +00:00
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 */
2008-09-19 07:03:59 +00:00
polling = 0 ; /*%%%*/
if ( XIO_RDSTREAM ( sock1 ) - > ignoreeof ) {
mayrd1 = 0 ;
}
2014-11-16 16:28:55 +00:00
if ( XIO_RDSTREAM ( sock2 ) - > ignoreeof ) {
mayrd2 = 0 ;
}
2008-09-19 07:03:59 +00:00
} else if ( polling & & wasaction ) {
wasaction = 0 ;
2008-01-27 12:00:08 +00:00
} 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 " ) ;
2015-01-06 12:07:15 +00:00
free ( buff ) ;
2008-01-27 12:00:08 +00:00
return 0 ;
}
if ( closing ) {
break ;
}
2023-06-12 21:01:54 +00:00
/* one possibility to come here is ignoreeof on some fd, but no EOF
2008-01-27 12:00:08 +00:00
and no data on any descriptor - this is no indication for end ! */
continue ;
}
if ( XIO_READABLE ( sock1 ) & & XIO_GETRDFD ( sock1 ) > = 0 & &
2008-09-26 06:13:19 +00:00
( fd1in - > revents /*&(POLLIN|POLLHUP|POLLERR)*/ ) ) {
if ( fd1in - > revents & POLLNVAL ) {
/* this is what we find on Mac OS X when poll()'ing on a device or
named pipe . a read ( ) might imm . return with 0 bytes , resulting
2023-06-12 21:01:54 +00:00
in a loop ? */
2008-09-26 06:13:19 +00:00
Error1 ( " poll(...[%d]: invalid request " , fd1in - > fd ) ;
2015-01-06 12:07:15 +00:00
free ( buff ) ;
2008-09-26 06:13:19 +00:00
return - 1 ;
}
2008-01-27 12:00:08 +00:00
mayrd1 = true ;
}
if ( XIO_READABLE ( sock2 ) & & XIO_GETRDFD ( sock2 ) > = 0 & &
2008-09-26 06:13:19 +00:00
( fd2in - > revents ) ) {
if ( fd2in - > revents & POLLNVAL ) {
Error1 ( " poll(...[%d]: invalid request " , fd2in - > fd ) ;
2015-01-06 12:07:15 +00:00
free ( buff ) ;
2008-09-26 06:13:19 +00:00
return - 1 ;
}
2008-01-27 12:00:08 +00:00
mayrd2 = true ;
}
2008-09-29 19:18:58 +00:00
if ( XIO_GETWRFD ( sock1 ) > = 0 & & fd1out - > fd > = 0 & & fd1out - > revents ) {
2008-09-26 06:13:19 +00:00
if ( fd1out - > revents & POLLNVAL ) {
Error1 ( " poll(...[%d]: invalid request " , fd1out - > fd ) ;
2015-01-06 12:07:15 +00:00
free ( buff ) ;
2008-09-26 06:13:19 +00:00
return - 1 ;
}
2008-01-27 12:00:08 +00:00
maywr1 = true ;
}
2008-09-29 19:18:58 +00:00
if ( XIO_GETWRFD ( sock2 ) > = 0 & & fd2out - > fd > = 0 & & fd2out - > revents ) {
2008-09-26 06:13:19 +00:00
if ( fd2out - > revents & POLLNVAL ) {
Error1 ( " poll(...[%d]: invalid request " , fd2out - > fd ) ;
2015-01-06 12:07:15 +00:00
free ( buff ) ;
2008-09-26 06:13:19 +00:00
return - 1 ;
}
2008-01-27 12:00:08 +00:00
maywr2 = true ;
}
if ( mayrd1 & & maywr2 ) {
mayrd1 = false ;
2020-12-29 04:07:03 +00:00
if ( ( bytes1 = xiotransfer ( sock1 , sock2 , buff , socat_opts . bufsiz , false ) )
2008-01-27 12:00:08 +00:00
< 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 ;
2008-09-04 21:42:52 +00:00
/* is more data available that has already passed poll()? */
2008-01-27 12:00:08 +00:00
mayrd1 = ( xiopending ( sock1 ) > 0 ) ;
if ( XIO_RDSTREAM ( sock1 ) - > readbytes ! = 0 & &
XIO_RDSTREAM ( sock1 ) - > actbytes = = 0 ) {
/* avoid idle when all readbytes already there */
mayrd1 = true ;
2008-09-20 21:01:10 +00:00
}
/* escape char occurred? */
if ( XIO_RDSTREAM ( sock1 ) - > actescape ) {
bytes1 = 0 ; /* indicate EOF */
}
2008-01-27 12:00:08 +00:00
}
/* (bytes1 == 0) handled later */
} else {
bytes1 = - 1 ;
}
if ( mayrd2 & & maywr1 ) {
mayrd2 = false ;
2020-12-29 04:07:03 +00:00
if ( ( bytes2 = xiotransfer ( sock2 , sock1 , buff , socat_opts . bufsiz , true ) )
2008-01-27 12:00:08 +00:00
< 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 ;
2008-09-04 21:42:52 +00:00
/* is more data available that has already passed poll()? */
2008-01-27 12:00:08 +00:00
mayrd2 = ( xiopending ( sock2 ) > 0 ) ;
if ( XIO_RDSTREAM ( sock2 ) - > readbytes ! = 0 & &
XIO_RDSTREAM ( sock2 ) - > actbytes = = 0 ) {
/* avoid idle when all readbytes already there */
mayrd2 = true ;
2023-06-12 21:01:54 +00:00
}
2008-09-20 21:01:10 +00:00
/* escape char occurred? */
if ( XIO_RDSTREAM ( sock2 ) - > actescape ) {
bytes2 = 0 ; /* indicate EOF */
}
2008-01-27 12:00:08 +00:00
}
/* (bytes2 == 0) handled later */
} else {
bytes2 = - 1 ;
}
/* NOW handle EOFs */
2008-09-19 07:03:59 +00:00
/*0 Debug4("bytes1=F_Zd, XIO_RDSTREAM(sock1)->eof=%d, XIO_RDSTREAM(sock1)->ignoreeof=%d, closing=%d",
bytes1 , XIO_RDSTREAM ( sock1 ) - > eof , XIO_RDSTREAM ( sock1 ) - > ignoreeof ,
closing ) ; */
2008-01-27 12:00:08 +00:00
if ( bytes1 = = 0 | | XIO_RDSTREAM ( sock1 ) - > eof > = 2 ) {
2008-09-20 21:01:10 +00:00
if ( XIO_RDSTREAM ( sock1 ) - > ignoreeof & &
! XIO_RDSTREAM ( sock1 ) - > actescape & & ! closing ) {
2008-01-27 12:00:08 +00:00
Debug1 ( " socket 1 (fd %d) is at EOF, ignoring " ,
XIO_RDSTREAM ( sock1 ) - > fd ) ; /*! */
2008-09-19 07:03:59 +00:00
mayrd1 = true ;
polling = 1 ; /* do not hook this eof fd to poll for pollintv*/
2019-12-30 09:05:33 +00:00
} else if ( XIO_RDSTREAM ( sock1 ) - > eof < = 2 ) {
2008-01-27 12:00:08 +00:00
Notice1 ( " socket 1 (fd %d) is at EOF " , XIO_GETRDFD ( sock1 ) ) ;
xioshutdown ( sock2 , SHUT_WR ) ;
2019-12-30 09:05:33 +00:00
XIO_RDSTREAM ( sock1 ) - > eof = 3 ;
2008-09-20 21:01:10 +00:00
XIO_RDSTREAM ( sock1 ) - > ignoreeof = false ;
2008-01-27 12:00:08 +00:00
}
2008-09-19 07:03:59 +00:00
} else if ( polling & & XIO_RDSTREAM ( sock1 ) - > ignoreeof ) {
polling = 0 ;
2008-01-27 12:00:08 +00:00
}
2008-10-28 20:07:47 +00:00
if ( XIO_RDSTREAM ( sock1 ) - > eof > = 2 ) {
if ( socat_opts . lefttoright ) {
break ;
}
closing = 1 ;
}
2008-01-27 12:00:08 +00:00
if ( bytes2 = = 0 | | XIO_RDSTREAM ( sock2 ) - > eof > = 2 ) {
2008-09-20 21:01:10 +00:00
if ( XIO_RDSTREAM ( sock2 ) - > ignoreeof & &
! XIO_RDSTREAM ( sock2 ) - > actescape & & ! closing ) {
2008-01-27 12:00:08 +00:00
Debug1 ( " socket 2 (fd %d) is at EOF, ignoring " ,
XIO_RDSTREAM ( sock2 ) - > fd ) ;
2008-09-19 07:03:59 +00:00
mayrd2 = true ;
polling = 1 ; /* do not hook this eof fd to poll for pollintv*/
2019-12-30 09:05:33 +00:00
} else if ( XIO_RDSTREAM ( sock2 ) - > eof < = 2 ) {
2008-01-27 12:00:08 +00:00
Notice1 ( " socket 2 (fd %d) is at EOF " , XIO_GETRDFD ( sock2 ) ) ;
xioshutdown ( sock1 , SHUT_WR ) ;
2019-12-30 09:05:33 +00:00
XIO_RDSTREAM ( sock2 ) - > eof = 3 ;
2008-09-20 21:01:10 +00:00
XIO_RDSTREAM ( sock2 ) - > ignoreeof = false ;
2008-01-27 12:00:08 +00:00
}
2008-09-19 07:03:59 +00:00
} else if ( polling & & XIO_RDSTREAM ( sock2 ) - > ignoreeof ) {
polling = 0 ;
2008-01-27 12:00:08 +00:00
}
2008-10-28 20:07:47 +00:00
if ( XIO_RDSTREAM ( sock2 ) - > eof > = 2 ) {
if ( socat_opts . righttoleft ) {
break ;
}
closing = 1 ;
}
2008-01-27 12:00:08 +00:00
}
/* close everything that's still open */
xioclose ( sock1 ) ;
xioclose ( sock2 ) ;
2020-12-29 04:07:03 +00:00
free ( buff ) ;
2008-01-27 12:00:08 +00:00
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 ;
2021-10-31 12:04:12 +00:00
# if HAVE_CLOCK_GETTIME
struct timespec now ;
# elif HAVE_PROTOTYPE_LIB_gettimeofday
2008-01-27 12:00:08 +00:00
struct timeval now ;
2021-10-31 12:04:12 +00:00
# endif /* !HAVE_PROTOTYPE_LIB_gettimeofday */
2008-01-27 12:00:08 +00:00
time_t nowt ;
2021-10-31 12:04:12 +00:00
int result ;
2008-01-27 12:00:08 +00:00
2021-10-31 12:04:12 +00:00
# if HAVE_CLOCK_GETTIME
result = clock_gettime ( CLOCK_REALTIME , & now ) ;
if ( result < 0 ) {
return result ;
}
nowt = now . tv_sec ;
# elif HAVE_PROTOTYPE_LIB_gettimeofday
result = Gettimeofday ( & now , NULL ) ;
2008-01-27 12:00:08 +00:00
if ( result < 0 ) {
return result ;
}
2021-10-31 12:04:12 +00:00
nowt = now . tv_sec ;
# else
nowt = time ( NULL ) ;
if ( nowt = = ( time_t ) - 1 ) {
2008-01-27 12:00:08 +00:00
return - 1 ;
2021-10-31 12:04:12 +00:00
}
# endif
2008-01-27 12:00:08 +00:00
# if HAVE_STRFTIME
2021-10-31 12:04:12 +00:00
bytes = strftime ( timestamp , 20 , " %Y/%m/%d %H:%M:%S " , localtime ( & nowt ) ) ;
# if HAVE_CLOCK_GETTIME
2022-01-02 12:08:29 +00:00
bytes + = sprintf ( timestamp + 19 , " . " F_tv_nsec " " , now . tv_nsec / 1000 ) ;
2021-10-31 12:04:12 +00:00
# elif HAVE_PROTOTYPE_LIB_gettimeofday
bytes + = sprintf ( timestamp + 19 , " . " F_tv_usec " " , now . tv_usec ) ;
2008-01-27 12:00:08 +00:00
# else
2021-10-31 12:04:12 +00:00
strncpy ( & timestamp [ bytes + + ] , " " , 2 ) ;
# endif
# else
strcpy ( timestamp , ctime ( & nowt ) ) ;
bytes = strlen ( timestamp ) ;
2008-01-27 12:00:08 +00:00
# endif
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 .
2008-09-20 21:01:10 +00:00
buff must be a malloc ( ) ' ed storage and might be realloc ( ) ' ed in this
2023-06-12 21:01:54 +00:00
function if more space is required after conversions .
2008-09-20 21:01:10 +00:00
Returns the number of bytes written , or 0 on EOF or < 0 if an
2008-01-27 12:00:08 +00:00
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 ,
2020-12-29 04:07:03 +00:00
unsigned char * buff , size_t bufsiz , bool righttoleft ) {
2008-09-20 21:01:10 +00:00
ssize_t bytes , writt = 0 ;
2023-06-10 09:09:01 +00:00
ssize_t sniffed ;
2008-01-27 12:00:08 +00:00
2020-12-29 04:07:03 +00:00
bytes = xioread ( inpipe , buff , bufsiz ) ;
2008-01-27 12:00:08 +00:00
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 ) {
2008-09-20 21:01:10 +00:00
;
2008-01-27 12:00:08 +00:00
} else if ( bytes = = 0 ) {
XIO_RDSTREAM ( inpipe ) - > eof = 2 ;
closing = MAX ( closing , 1 ) ;
}
2008-09-20 21:01:10 +00:00
if ( bytes > 0 ) {
2023-10-26 16:42:41 +00:00
# if WITH_STATS
+ + XIO_RDSTREAM ( inpipe ) - > blocks_read ;
XIO_RDSTREAM ( inpipe ) - > bytes_read + = bytes ;
# endif
2008-09-20 21:01:10 +00:00
/* handle escape char */
if ( XIO_RDSTREAM ( inpipe ) - > escape ! = - 1 ) {
/* check input data for escape char */
2020-12-29 04:07:03 +00:00
unsigned char * ptr = buff ;
2008-09-20 21:01:10 +00:00
size_t ctr = 0 ;
while ( ctr < bytes ) {
if ( * ptr = = XIO_RDSTREAM ( inpipe ) - > escape ) {
/* found: set flag, truncate input data */
XIO_RDSTREAM ( inpipe ) - > actescape = true ;
bytes = ctr ;
Info ( " escape char found in input " ) ;
break ;
}
+ + ptr ; + + ctr ;
}
if ( ctr ! = bytes ) {
XIO_RDSTREAM ( inpipe ) - > eof = 2 ;
}
}
}
if ( bytes > 0 ) {
2008-01-27 12:00:08 +00:00
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 ;
}
2023-10-26 14:57:39 +00:00
if ( ! righttoleft & & sniffleft > = 0 ) {
if ( ( sniffed = Write ( sniffleft , buff , bytes ) ) < bytes ) {
2023-06-10 09:09:01 +00:00
if ( sniffed < 0 )
Warn3 ( " -r: write(%d, buff, " F_Zu " ): %s " ,
2023-10-26 14:57:39 +00:00
sniffleft , bytes , strerror ( errno ) ) ;
2023-06-10 09:09:01 +00:00
else if ( sniffed < bytes )
Warn3 ( " -r: write(%d, buff, " F_Zu " ) -> " F_Zd ,
2023-10-26 14:57:39 +00:00
sniffleft , bytes , sniffed ) ;
2023-06-10 09:09:01 +00:00
}
2023-10-26 14:57:39 +00:00
} else if ( righttoleft & & sniffright > = 0 ) {
if ( ( sniffed = Write ( sniffright , buff , bytes ) ) < bytes ) {
2023-06-10 09:09:01 +00:00
if ( sniffed < 0 )
Warn3 ( " -R: write(%d, buff, " F_Zu " ): %s " ,
2023-10-26 14:57:39 +00:00
sniffright , bytes , strerror ( errno ) ) ;
2023-06-10 09:09:01 +00:00
else if ( sniffed < bytes )
Warn3 ( " -R: write(%d, buff, " F_Zu " ) -> " F_Zd ,
2023-10-26 14:57:39 +00:00
sniffright , bytes , sniffed ) ;
2023-06-10 09:09:01 +00:00
}
2020-12-31 12:19:19 +00:00
}
2008-01-27 12:00:08 +00:00
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 ;
2020-12-29 04:07:03 +00:00
s = buff ;
end = buff + bytes ;
2008-01-27 12:00:08 +00:00
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 ) {
2020-12-29 04:07:03 +00:00
int c = buff [ i ] ;
if ( i > 0 & & buff [ i - 1 ] = = ' \n ' )
2008-01-27 12:00:08 +00:00
/*! 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 ;
2010-12-08 09:58:25 +00:00
/* print prefix */
xioprintblockheader ( stderr , bytes , righttoleft ) ;
2008-01-27 12:00:08 +00:00
for ( i = 0 ; i < bytes ; + + i ) {
2020-12-29 04:07:03 +00:00
fprintf ( stderr , " %02x " , buff [ i ] ) ;
2008-01-27 12:00:08 +00:00
}
fputc ( ' \n ' , stderr ) ;
}
2020-12-29 04:07:03 +00:00
writt = xiowrite ( outpipe , buff , bytes ) ;
2008-01-27 12:00:08 +00:00
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 ) ) ;
2023-10-26 16:42:41 +00:00
# if WITH_STATS
+ + XIO_WRSTREAM ( outpipe ) - > blocks_written ;
XIO_WRSTREAM ( outpipe ) - > bytes_written + = writt ;
# endif
2008-01-27 12:00:08 +00:00
}
}
return writt ;
}
# define CR '\r'
# define LF '\n'
2008-09-20 21:01:10 +00:00
/* converts the newline characters (or character sequences) from the one
specified in lineterm1 to that of lineterm2 . Possible values are
LINETERM_CR , LINETERM_CRNL , LINETERM_RAW .
2020-12-29 04:07:03 +00:00
bytes specifies the number of bytes input and output */
int cv_newline ( unsigned char * buff , ssize_t * bytes ,
2008-01-27 12:00:08 +00:00
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 ' ;
}
2020-12-29 04:07:03 +00:00
z = buff + * bytes ;
p = buff ;
2008-01-27 12:00:08 +00:00
while ( p < z ) {
if ( * p = = from ) * p = to ;
+ + p ;
}
} else if ( lineterm1 = = LINETERM_CRNL ) {
2020-12-29 04:07:03 +00:00
/* buffer might become shorter */
2008-01-27 12:00:08 +00:00
unsigned char to , * s , * t , * z ;
if ( lineterm2 = = LINETERM_RAW ) {
to = ' \n ' ;
} else {
to = ' \r ' ;
}
2020-12-29 04:07:03 +00:00
z = buff + * bytes ;
s = t = buff ;
2008-01-27 12:00:08 +00:00
while ( s < z ) {
if ( * s = = ' \r ' ) {
+ + s ;
continue ;
}
if ( * s = = ' \n ' ) {
* t + + = to ; + + s ;
} else {
* t + + = * s + + ;
}
}
2020-12-29 04:07:03 +00:00
* bytes = t - buff ;
2008-01-27 12:00:08 +00:00
} else {
2020-12-29 04:07:03 +00:00
/* buffer becomes longer (up to double length), must alloc another space */
static unsigned char * buf2 ; /*! not threadsafe */
2008-01-27 12:00:08 +00:00
unsigned char from ; unsigned char * s , * t , * z ;
2020-12-29 04:07:03 +00:00
2008-01-27 12:00:08 +00:00
if ( lineterm1 = = LINETERM_RAW ) {
from = ' \n ' ;
} else {
from = ' \r ' ;
}
2020-12-29 04:07:03 +00:00
if ( buf2 = = NULL ) {
if ( ( buf2 = Malloc ( socat_opts . bufsiz ) ) = = NULL ) {
return - 1 ;
}
2008-01-27 12:00:08 +00:00
}
2020-12-29 04:07:03 +00:00
memcpy ( buf2 , buff , * bytes ) ;
s = buf2 ; t = buff ; z = buf2 + * bytes ;
2008-01-27 12:00:08 +00:00
while ( s < z ) {
if ( * s = = from ) {
* t + + = ' \r ' ; * t + + = ' \n ' ;
+ + s ;
continue ;
} else {
* t + + = * s + + ;
}
}
2020-12-29 04:07:03 +00:00
* bytes = t - buff ; ;
2008-01-27 12:00:08 +00:00
}
return 0 ;
}
void socat_signal ( int signum ) {
2015-01-12 20:46:16 +00:00
int _errno ;
_errno = errno ;
diag_in_handler = 1 ;
Notice1 ( " socat_signal(): handling signal %d " , signum ) ;
2008-01-27 12:00:08 +00:00
switch ( signum ) {
2023-06-17 13:31:26 +00:00
default :
2016-05-11 18:34:33 +00:00
diag_immediate_exit = 1 ;
case SIGQUIT :
2008-01-27 12:00:08 +00:00
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 ;
2023-06-12 21:01:54 +00:00
case SIGHUP :
2008-01-27 12:00:08 +00:00
case SIGINT :
Notice1 ( " exiting on signal %d " , signum ) ; break ;
}
2015-01-12 20:46:16 +00:00
Notice1 ( " socat_signal(): finishing signal %d " , signum ) ;
2023-06-12 10:28:48 +00:00
diag_exit ( 128 + signum ) ; /* internal cleanup + _exit() */
2015-01-12 20:46:16 +00:00
diag_in_handler = 0 ;
errno = _errno ;
2008-01-27 12:00:08 +00:00
}
/* 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 ) {
2015-01-12 20:46:16 +00:00
if ( ! diag_in_handler ) {
Warn2 ( " unlink( \" %s \" ): %s " ,
socat_opts . lock . lockfile , strerror ( errno ) ) ;
} else {
Warn1 ( " unlink( \" %s \" ): " F_strerror ,
socat_opts . lock . lockfile ) ;
}
2008-01-27 12:00:08 +00:00
} 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 ;
}
2023-10-26 16:42:41 +00:00
# if WITH_STATS
void socat_signal_logstats ( int signum ) {
diag_in_handler = 1 ;
Notice1 ( " socat_signal_logstats(): handling signal %d " , signum ) ;
socat_print_stats ( ) ;
Notice1 ( " socat_signal_logstats(): finishing signal %d " , signum ) ;
diag_in_handler = 0 ;
}
# endif /* WITH_STATS */
# if WITH_STATS
static void socat_print_stats ( void )
{
const char ltorf0 [ ] = " STATISTICS: left to right: %%%ullu packets(s), %%%ullu byte(s) " ;
const char rtolf0 [ ] = " STATISTICS: right to left: %%%ullu packets(s), %%%ullu byte(s) " ;
char ltorf1 [ sizeof ( ltorf0 ) ] ; /* final printf format with lengths of number */
char rtolf1 [ sizeof ( rtolf0 ) ] ; /* final printf format with lengths of number */
unsigned int blocksd = 1 , bytesd = 1 ; /* number of digits in output */
struct single * sock1w , * sock2w ;
int savelevel ;
if ( sock1 = = NULL | | sock2 = = NULL ) {
Warn ( " transfer engine not yet started, statistics not available " ) ;
return ;
}
if ( ( sock1 - > tag & ~ XIO_TAG_CLOSED ) = = XIO_TAG_DUAL ) {
sock1w = sock1 - > dual . stream [ 1 ] ;
} else {
sock1w = & sock1 - > stream ;
}
if ( ( sock2 - > tag & ~ XIO_TAG_CLOSED ) = = XIO_TAG_DUAL ) {
sock2w = sock2 - > dual . stream [ 1 ] ;
} else {
sock2w = & sock2 - > stream ;
}
if ( ! socat_opts . righttoleft & & ! socat_opts . righttoleft ) {
/* Both directions - format output */
unsigned long long int maxblocks =
Max ( sock1w - > blocks_written , sock2w - > blocks_written ) ;
unsigned long long int maxbytes =
Max ( sock1w - > bytes_written , sock2w - > bytes_written ) ;
/* Calculate number of digits */
while ( maxblocks > = 10 ) { + + blocksd ; maxblocks / = 10 ; }
while ( maxbytes > = 10 ) { + + bytesd ; maxbytes / = 10 ; }
}
snprintf ( ltorf1 , sizeof ( ltorf1 ) , ltorf0 , blocksd , bytesd ) ;
snprintf ( rtolf1 , sizeof ( rtolf1 ) , rtolf0 , blocksd , bytesd ) ;
/* Statistics are E_INFO level; make sure they are printed anyway */
savelevel = diag_get_int ( ' d ' ) ;
diag_set_int ( ' d ' , E_INFO ) ;
Warn ( " statistics are experimental " ) ;
if ( ! socat_opts . righttoleft ) {
Info2 ( ltorf1 , sock2w - > blocks_written , sock2w - > bytes_written ) ;
}
if ( ! socat_opts . lefttoright ) {
Info2 ( rtolf1 , sock1w - > blocks_written , sock1w - > bytes_written ) ;
}
diag_set_int ( ' d ' , savelevel ) ;
return ;
}
# endif /* WITH_STATs */