diff --git a/CHANGES b/CHANGES index dfe9314..042c977 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +new features: + new address option "escape" allows to break a socat instance even when + raw terminal mode prevents ^C etc. + corrections: some raw IP and UNIX datagram modes failed on BSD systems diff --git a/VERSION b/VERSION index 9a06c88..f509df5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock" +"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock+escape" diff --git a/doc/socat.yo b/doc/socat.yo index 4befd5b..d422aef 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -10,7 +10,7 @@ def(Filan)(0)(bf(Filan)) def(procan)(0)(bf(procan)) def(Procan)(0)(bf(Procan)) -manpage(socat)(1)(Feb 2008)(socat)() +manpage(socat)(1)(Jul 2008)(socat)() whenhtml( label(CONTENTS) @@ -1516,6 +1516,10 @@ label(OPTION_LOCKFILE)dit(bf(tt(lockfile=))) label(OPTION_WAITLOCK)dit(bf(tt(waitlock=))) If lockfile exists, waits until it disappears. When lockfile does not exist, creates it and continues, unlinks lockfile on exit. +label(OPTION_ESCAPE)dit(bf(tt(escape=))) + Specifies the numeric code of a character that triggers EOF on the input + stream. It is useful with a terminal in raw mode + (link(example)(EXAMPLE_OPTION_ESCAPE)). enddit() startdit()enddit()nl() @@ -2630,13 +2634,15 @@ at most 512 data bytes per packet (link(mss)(OPTION_MSS)). label(EXAMPLE_ADDRESS_GOPEN) label(EXAMPLE_OPTION_RAW) label(EXAMPLE_OPTION_ECHO) -dit(bf(tt(socat - /dev/ttyS0,raw,echo=0,crnl))) +label(EXAMPLE_OPTION_ESCAPE) +dit(bf(tt(socat -,raw,echo=0,escape=0x0f /dev/ttyS0,raw,echo=0,crnl))) Opens an interactive connection via the serial line, e.g. for talking with a -modem. link(raw)(OPTION_RAW) and link(echo)(OPTION_ECHO) set ttyS0's terminal -parameters to practicable values, link(crnl)(OPTION_CRNL) -converts to correct newline characters. Consider using -link(READLINE)(ADDRESS_READLINE) instead of `-'. +modem. link(raw)(OPTION_RAW) and link(echo)(OPTION_ECHO) set the console's and +ttyS0's terminal parameters to practicable values, link(crnl)(OPTION_CRNL) +converts to correct newline characters. link(escape)(OPTION_ESCAPE) allows to +terminate the socat process with character control-O. +Consider using link(READLINE)(ADDRESS_READLINE) instead of the first address. label(EXAMPLE_ADDRESS_UNIX_LISTEN) diff --git a/socat.c b/socat.c index 3128ec9..3ff3d17 100644 --- a/socat.c +++ b/socat.c @@ -947,7 +947,11 @@ int _socat(void) { XIO_RDSTREAM(sock1)->actbytes == 0) { /* avoid idle when all readbytes already there */ mayrd1 = true; - } + } + /* escape char occurred? */ + if (XIO_RDSTREAM(sock1)->actescape) { + bytes1 = 0; /* indicate EOF */ + } } /* (bytes1 == 0) handled later */ } else { @@ -976,6 +980,10 @@ int _socat(void) { /* avoid idle when all readbytes already there */ mayrd2 = true; } + /* escape char occurred? */ + if (XIO_RDSTREAM(sock2)->actescape) { + bytes2 = 0; /* indicate EOF */ + } } /* (bytes2 == 0) handled later */ } else { @@ -988,7 +996,8 @@ int _socat(void) { bytes1, XIO_RDSTREAM(sock1)->eof, XIO_RDSTREAM(sock1)->ignoreeof, closing);*/ if (bytes1 == 0 || XIO_RDSTREAM(sock1)->eof >= 2) { - if (XIO_RDSTREAM(sock1)->ignoreeof && !closing) { + if (XIO_RDSTREAM(sock1)->ignoreeof && + !XIO_RDSTREAM(sock1)->actescape && !closing) { Debug1("socket 1 (fd %d) is at EOF, ignoring", XIO_RDSTREAM(sock1)->fd); /*! */ mayrd1 = true; @@ -996,16 +1005,20 @@ int _socat(void) { } else { Notice1("socket 1 (fd %d) is at EOF", XIO_GETRDFD(sock1)); xioshutdown(sock2, SHUT_WR); + XIO_RDSTREAM(sock1)->eof = 2; + XIO_RDSTREAM(sock1)->ignoreeof = false; if (socat_opts.lefttoright) { break; } + closing = 1; } } else if (polling && XIO_RDSTREAM(sock1)->ignoreeof) { polling = 0; } if (bytes2 == 0 || XIO_RDSTREAM(sock2)->eof >= 2) { - if (XIO_RDSTREAM(sock2)->ignoreeof && !closing) { + if (XIO_RDSTREAM(sock2)->ignoreeof && + !XIO_RDSTREAM(sock2)->actescape && !closing) { Debug1("socket 2 (fd %d) is at EOF, ignoring", XIO_RDSTREAM(sock2)->fd); mayrd2 = true; @@ -1013,9 +1026,12 @@ int _socat(void) { } else { Notice1("socket 2 (fd %d) is at EOF", XIO_GETRDFD(sock2)); xioshutdown(sock1, SHUT_WR); + XIO_RDSTREAM(sock2)->eof = 2; + XIO_RDSTREAM(sock2)->ignoreeof = false; if (socat_opts.righttoleft) { break; } + closing = 1; } } else if (polling && XIO_RDSTREAM(sock2)->ignoreeof) { polling = 0; @@ -1102,8 +1118,9 @@ static int /* 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 + buff must be a malloc()'ed storage and might be realloc()'ed in this + function if more space is required after 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. @@ -1113,9 +1130,9 @@ static int /* 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; + ssize_t bytes, writt = 0; - bytes = xioread(inpipe, *buff, socat_opts.bufsiz); + bytes = xioread(inpipe, *buff, bufsiz); if (bytes < 0) { if (errno != EAGAIN) XIO_RDSTREAM(inpipe)->eof = 2; @@ -1123,14 +1140,35 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, 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 (bytes > 0) { + /* handle escape char */ + if (XIO_RDSTREAM(inpipe)->escape != -1) { + /* check input data for escape char */ + unsigned char *ptr = *buff; + 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) { if (XIO_RDSTREAM(inpipe)->lineterm != XIO_WRSTREAM(outpipe)->lineterm) { @@ -1248,8 +1286,14 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, #define LF '\n' -int cv_newline(unsigned char **buff, ssize_t *bytes, +/* 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. + buff points to the malloc()'ed data, input and output. It may be subject to + realloc(). bytes specifies the number of bytes input and output */ +int cv_newline(unsigned char **buff, ssize_t *bufsiz, int lineterm1, int lineterm2) { + ssize_t *bytes = bufsiz; /* must perform newline changes */ if (lineterm1 <= LINETERM_CR && lineterm2 <= LINETERM_CR) { /* no change in data length */ @@ -1287,7 +1331,7 @@ int cv_newline(unsigned char **buff, ssize_t *bytes, *t++ = *s++; } } - *bytes = t - *buff; + *bufsiz = t - *buff; } else { /* buffer becomes longer, must alloc another space */ unsigned char *buf2; @@ -1297,7 +1341,7 @@ int cv_newline(unsigned char **buff, ssize_t *bytes, } else { from = '\r'; } - if ((buf2 = Malloc(2*socat_opts.bufsiz+1)) == NULL) { + if ((buf2 = Malloc(2*socat_opts.bufsiz/*sic!*/+1)) == NULL) { return -1; } s = *buff; t = buf2; z = *buff + *bytes; @@ -1312,7 +1356,7 @@ int cv_newline(unsigned char **buff, ssize_t *bytes, } free(*buff); *buff = buf2; - *bytes = t - buf2;; + *bufsiz = t - buf2;; } return 0; } diff --git a/test.sh b/test.sh index 6613a59..054673f 100755 --- a/test.sh +++ b/test.sh @@ -8450,6 +8450,69 @@ esac N=$((N+1)) +# test the escape option +NAME=ESCAPE +case "$TESTS" in +*%functions%*|*%escape%*|*%$NAME%*) +TEST="$NAME: escape character triggers EOF" +# idea: start socat just echoing input, but apply escape option. send a string +# containing the escape character and check if the output is truncated +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD="$SOCAT $opts -,escape=27 pipe" +printf "test $F_n $TEST... " $N +$ECHO "$da\n\x1bXYZ" |$CMD >"$tf" 2>"$te" +if [ $? -ne 0 ]; then + $PRINTF "$FAILED: $SOCAT:\n" + echo "$CMD" + cat "$te" + numFAIL=$((numFAIL+1)) +elif ! echo "$da" |diff - "$tf" >"$tdiff"; then + $PRINTF "$FAILED: diff:\n" + cat "$tdiff" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then cat $te; fi + numOK=$((numOK+1)) +fi +esac +N=$((N+1)) + +# test the escape option combined with ignoreeof +NAME=ESCAPE_IGNOREEOF +case "$TESTS" in +*%functions%*|*%ignoreeof%*|*%escape%*|*%$NAME%*) +TEST="$NAME: escape character triggers EOF" +# idea: start socat just echoing input, but apply escape option. send a string +# containing the escape character and check if the output is truncated +ti="$td/test$N.file" +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD="$SOCAT -T 5 $opts file:$ti,ignoreeof,escape=27!!- pipe" +printf "test $F_n $TEST... " $N +>"$ti" +$CMD >"$tf" 2>"$te" & +$ECHO "$da\n\x1bXYZ" >>"$ti" +sleep 1 +if ! echo "$da" |diff - "$tf" >"$tdiff"; then + $PRINTF "$FAILED: diff:\n" + cat "$tdiff" + cat "$te" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then cat $te; fi + numOK=$((numOK+1)) +fi +esac +N=$((N+1)) + + echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed" if [ "$numFAIL" -gt 0 ]; then diff --git a/xio-tun.c b/xio-tun.c index 1bc8709..dbc86cc 100644 --- a/xio-tun.c +++ b/xio-tun.c @@ -1,5 +1,5 @@ /* source: xio-tun.c */ -/* Copyright Gerhard Rieger 2007 */ +/* Copyright Gerhard Rieger 2007-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for opening addresses of tun/tap type */ @@ -17,8 +17,6 @@ static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3); -#define XIO_OFFSETOF(x) ((size_t)&((xiosingle_t *)0)->x) - /****** TUN addresses ******/ const struct optdesc opt_tun_device = { "tun-device", NULL, OPT_TUN_DEVICE, GROUP_TUN, PH_OPEN, TYPE_FILENAME, OFUNC_SPEC }; const struct optdesc opt_tun_name = { "tun-name", NULL, OPT_TUN_NAME, GROUP_INTERFACE, PH_FD, TYPE_STRING, OFUNC_SPEC }; diff --git a/xio.h b/xio.h index f5b1926..930d6bd 100644 --- a/xio.h +++ b/xio.h @@ -167,6 +167,8 @@ typedef struct single { const char *name; /* only with END_UNLINK */ int (*sigchild)(struct single *); /* callback after sigchild */ pid_t ppid; /* parent pid, only if we send it signals */ + int escape; /* escape character; -1 for no escape */ + bool actescape; /* escape character found in input data */ union { struct { int fdout; /* use fd for output */ diff --git a/xiolayer.c b/xiolayer.c index 4801b92..7ae6bd3 100644 --- a/xiolayer.c +++ b/xiolayer.c @@ -16,6 +16,7 @@ const struct optdesc opt_crnl = { "crnl", NULL, OPT_CRNL, GROUP_A const struct optdesc opt_readbytes = { "readbytes", "bytes", OPT_READBYTES, GROUP_APPL, PH_LATE, TYPE_SIZE_T, OFUNC_EXT, (int)(&((struct single *)0)->readbytes), sizeof(((struct single *)0)->readbytes) }; const struct optdesc opt_lockfile = { "lockfile", NULL, OPT_LOCKFILE, GROUP_APPL, PH_INIT, TYPE_FILENAME, OFUNC_EXT, 0, 0 }; const struct optdesc opt_waitlock = { "waitlock", NULL, OPT_WAITLOCK, GROUP_APPL, PH_INIT, TYPE_FILENAME, OFUNC_EXT, 0, 0 }; +const struct optdesc opt_escape = { "escape", NULL, OPT_ESCAPE, GROUP_APPL, PH_INIT, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(escape), sizeof(((xiosingle_t *)0)->escape) }; /****** APPL addresses ******/ #if WITH_RETRY const struct optdesc opt_forever = { "forever", NULL, OPT_FOREVER, GROUP_RETRY, PH_INIT, TYPE_BOOL, OFUNC_EXT, (int)(&((struct single *)0)->forever), sizeof(((struct single *)0)->forever) }; diff --git a/xiolayer.h b/xiolayer.h index 926dede..4ec2c5e 100644 --- a/xiolayer.h +++ b/xiolayer.h @@ -1,5 +1,5 @@ /* source: xiolayer.h */ -/* Copyright Gerhard Rieger 2001-2005 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xiolayer_h_included @@ -11,6 +11,7 @@ extern const struct optdesc opt_crnl; extern const struct optdesc opt_readbytes; extern const struct optdesc opt_lockfile; extern const struct optdesc opt_waitlock; +extern const struct optdesc opt_escape; extern const struct optdesc opt_forever; extern const struct optdesc opt_intervall; extern const struct optdesc opt_retry; diff --git a/xioopen.c b/xioopen.c index f944740..76f3a91 100644 --- a/xioopen.c +++ b/xioopen.c @@ -1,5 +1,5 @@ /* source: xioopen.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this is the source file of the extended open function */ @@ -301,6 +301,7 @@ static xiofile_t *xioallocfd(void) { #endif /* WITH_SOCKET */ fd->stream.howtoend = END_UNSPEC; /* fd->stream.name = NULL; */ + fd->stream.escape = -1; /* fd->stream.para.exec.pid = 0; */ fd->stream.lineterm = LINETERM_RAW; diff --git a/xioopts.c b/xioopts.c index ad76434..dfbb1ef 100644 --- a/xioopts.c +++ b/xioopts.c @@ -397,6 +397,7 @@ const struct optname optionnames[] = { IF_TERMIOS("eol2", &opt_veol2) IF_TERMIOS("erase", &opt_verase) IF_SOCKET ("error", &opt_so_error) + IF_ANY ("escape", &opt_escape) IF_OPEN ("excl", &opt_o_excl) #if WITH_EXT2 && defined(EXT2_APPEND_FL) IF_ANY ("ext2-append", &opt_ext2_append) @@ -3339,6 +3340,8 @@ static int applyopt_offset(struct single *xfd, struct opt *opt) { switch (opt->desc->type) { case TYPE_BOOL: *(bool *)ptr = opt->value.u_bool; break; + case TYPE_INT: + *(int *)ptr = opt->value.u_int; break; case TYPE_DOUBLE: *(double *)ptr = opt->value.u_double; break; case TYPE_TIMEVAL: diff --git a/xioopts.h b/xioopts.h index 849cb46..81ff412 100644 --- a/xioopts.h +++ b/xioopts.h @@ -9,6 +9,8 @@ #define ODESC_DONE ((void *)-1) /* indicates that option has been applied */ #define ODESC_ERROR ODESC_DONE /* maybe later */ +#define XIO_OFFSETOF(x) ((size_t)&((xiosingle_t *)0)->x) + /* atomic structure for use in the option search table; keep compatible with struct wordent! */ struct optname { @@ -250,6 +252,7 @@ enum e_optcode { OPT_ECHOPRT, /* termios.c_lflag */ #endif OPT_END_CLOSE, /* xfd.stream.howtoend = END_CLOSE */ + OPT_ESCAPE, OPT_EXT2_SECRM, OPT_EXT2_UNRM, OPT_EXT2_COMPR,