Socat option -r,-R path specifications allow use of variables

This commit is contained in:
Gerhard Rieger 2023-10-26 16:57:39 +02:00
parent 9faf068949
commit 2af6089436
9 changed files with 378 additions and 65 deletions

View file

@ -37,6 +37,10 @@ Features:
Test: RCVTIMEO_DTLS Test: RCVTIMEO_DTLS
Feature proposed by Vladimir Nikishkin. Feature proposed by Vladimir Nikishkin.
The file names with -r and -R now may contain environment variable
references.
Test: VARS_IN_SNIFFPATH
Corrections: Corrections:
When a sub process (EXEC, SYSTEM) terminated with exit code other than When a sub process (EXEC, SYSTEM) terminated with exit code other than
0, its last sent data might have been lost depending on timing of read/ 0, its last sent data might have been lost depending on timing of read/

View file

@ -161,12 +161,16 @@ dit(bf(tt(-x)))
Writes the transferred data not only to their target streams, but also to Writes the transferred data not only to their target streams, but also to
stderr. The output format is hexadecimal, prefixed with "> " or "< " stderr. The output format is hexadecimal, prefixed with "> " or "< "
indicating flow directions. Can be combined with code(-v). indicating flow directions. Can be combined with code(-v).
dit(bf(tt(-r <file>))) label(option_r)dit(bf(tt(-r <file>)))
Dumps the raw (binary) data flowing from left to right address to the given Dumps the raw (binary) data flowing from left to right address to the given
file. file. The file name may contain references to environment variables and
code($$) (pid), code($PROGNAME) (see option link(option -lp)(option_lp)),
code($TIMESTAMP) (uses format %Y%m%dT%H%M%S), and code(MICROS) (microseconds
of daytime). These references have to be protected from shell expansion of
course.
dit(bf(tt(-R <file>))) dit(bf(tt(-R <file>)))
Dumps the raw (binary) data flowing from right to left address to the given Dumps the raw (binary) data flowing from right to left address to the given
file. file. See link(option -r)(option_r) for customization of file name.
label(option_b)dit(bf(tt(-b))tt(<size>)) label(option_b)dit(bf(tt(-b))tt(<size>))
Sets the data transfer block <size> [link(size_t)(TYPE_SIZE_T)]. Sets the data transfer block <size> [link(size_t)(TYPE_SIZE_T)].
At most <size> bytes are transferred per step. Default is 8192 bytes. At most <size> bytes are transferred per step. Default is 8192 bytes.

94
socat.c
View file

@ -36,8 +36,6 @@ struct {
char logopt; /* y..syslog; s..stderr; f..file; m..mixed */ char logopt; /* y..syslog; s..stderr; f..file; m..mixed */
bool lefttoright; /* first addr ro, second addr wo */ bool lefttoright; /* first addr ro, second addr wo */
bool righttoleft; /* first addr wo, second addr ro */ bool righttoleft; /* first addr wo, second addr ro */
int sniffleft; /* -1 or an FD for teeing data arriving on xfd1 */
int sniffright; /* -1 or an FD for teeing data arriving on xfd2 */
xiolock_t lock; /* a lock file */ xiolock_t lock; /* a lock file */
unsigned long log_sigs; /* signals to be caught just for logging */ unsigned long log_sigs; /* signals to be caught just for logging */
} socat_opts = { } socat_opts = {
@ -52,8 +50,6 @@ struct {
's', /* logopt */ 's', /* logopt */
false, /* lefttoright */ false, /* lefttoright */
false, /* righttoleft */ false, /* righttoleft */
-1, /* sniffleft */
-1, /* sniffright */
{ NULL, 0 }, /* lock */ { NULL, 0 }, /* lock */
1<<SIGHUP | 1<<SIGINT | 1<<SIGQUIT | 1<<SIGILL | 1<<SIGABRT | 1<<SIGBUS | 1<<SIGFPE | 1<<SIGSEGV | 1<<SIGTERM, /* log_sigs */ 1<<SIGHUP | 1<<SIGINT | 1<<SIGQUIT | 1<<SIGILL | 1<<SIGABRT | 1<<SIGBUS | 1<<SIGFPE | 1<<SIGSEGV | 1<<SIGTERM, /* log_sigs */
}; };
@ -61,7 +57,6 @@ struct {
void socat_usage(FILE *fd); void socat_usage(FILE *fd);
void socat_opt_hint(FILE *fd, char a, char b); void socat_opt_hint(FILE *fd, char a, char b);
void socat_version(FILE *fd); void socat_version(FILE *fd);
static int xio_openauxwr(const char *path, const char *hint);
int socat(const char *address1, const char *address2); int socat(const char *address1, const char *address2);
int _socat(void); int _socat(void);
int cv_newline(unsigned char *buff, ssize_t *bytes, int lineterm1, int lineterm2); int cv_newline(unsigned char *buff, ssize_t *bytes, int lineterm1, int lineterm2);
@ -213,9 +208,7 @@ int main(int argc, const char *argv[]) {
break; break;
} }
} }
if ((socat_opts.sniffleft = xio_openauxwr(a, "option -r")) < 0) { xiosetopt('r', a);
Error2("option -r: failed to open \"%s\": %s", a, strerror(errno));
}
break; break;
case 'R': if (arg1[0][2]) { case 'R': if (arg1[0][2]) {
a = *arg1+2; a = *arg1+2;
@ -226,9 +219,7 @@ int main(int argc, const char *argv[]) {
break; break;
} }
} }
if ((socat_opts.sniffright = xio_openauxwr(a, "option -R")) < 0) { xiosetopt('R', a);
Error2("option -R: failed to open \"%s\": %s", a, strerror(errno));
}
break; break;
case 'b': if (arg1[0][2]) { case 'b': if (arg1[0][2]) {
a = *arg1+2; a = *arg1+2;
@ -655,50 +646,11 @@ void socat_version(FILE *fd) {
} }
/* Opens a path for logging or tracing.
Return the filedescriptor, or -1 when an error occurred (errn0).
Prints messages but no Error().
*/
static int xio_openauxwr(const char *path, const char *hint)
{
int fd;
int _errno;
if ((fd = Open(path, O_CREAT|O_WRONLY|O_APPEND|
#ifdef O_LARGEFILE
O_LARGEFILE|
#endif
#ifdef O_CLOEXEC
O_CLOEXEC|
#endif
O_NONBLOCK, 0664)) < 0) {
if (errno != ENXIO)
return -1;
/* Possibly a named pipe that does not yet have a reader */
_errno = errno;
Notice3("%s \"%s\": %s, retrying r/w", hint, path, strerror(errno));
if ((fd = Open(path, O_CREAT|O_RDWR|O_APPEND|
#ifdef O_LARGEFILE
O_LARGEFILE|
#endif
#ifdef O_CLOEXEC
O_CLOEXEC|
#endif
O_NONBLOCK, 0664)) < 0) {
errno = _errno;
return -1;
}
}
#ifndef O_CLOEXEC
Fcntl_l(fd, F_SETFD, FD_CLOEXEC);
#endif
return fd;
}
xiofile_t *sock1, *sock2; xiofile_t *sock1, *sock2;
int closing = 0; /* 0..no eof yet, 1..first eof just occurred, int closing = 0; /* 0..no eof yet, 1..first eof just occurred,
2..counting down closing timeout */ 2..counting down closing timeout */
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 */
/* call this function when the common command line options are parsed, and the /* call this function when the common command line options are parsed, and the
addresses are extracted (but not resolved). */ addresses are extracted (but not resolved). */
@ -861,6 +813,28 @@ int _socat(void) {
int wasaction = 1; /* last poll was active, do NOT sleep before next */ int wasaction = 1; /* last poll was active, do NOT sleep before next */
struct timeval total_timeout; /* the actual total timeout timer */ struct timeval total_timeout; /* the actual total timeout timer */
{
/* 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));
}
}
}
#if WITH_FILAN #if WITH_FILAN
if (socat_opts.debug) { if (socat_opts.debug) {
int fdi, fdo; int fdi, fdo;
@ -1398,23 +1372,23 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
errno = EAGAIN; return -1; errno = EAGAIN; return -1;
} }
if (!righttoleft && socat_opts.sniffleft >= 0) { if (!righttoleft && sniffleft >= 0) {
if ((sniffed = Write(socat_opts.sniffleft, buff, bytes)) < bytes) { if ((sniffed = Write(sniffleft, buff, bytes)) < bytes) {
if (sniffed < 0) if (sniffed < 0)
Warn3("-r: write(%d, buff, "F_Zu"): %s", Warn3("-r: write(%d, buff, "F_Zu"): %s",
socat_opts.sniffleft, bytes, strerror(errno)); sniffleft, bytes, strerror(errno));
else if (sniffed < bytes) else if (sniffed < bytes)
Warn3("-r: write(%d, buff, "F_Zu") -> "F_Zd, Warn3("-r: write(%d, buff, "F_Zu") -> "F_Zd,
socat_opts.sniffleft, bytes, sniffed); sniffleft, bytes, sniffed);
} }
} else if (righttoleft && socat_opts.sniffright >= 0) { } else if (righttoleft && sniffright >= 0) {
if ((sniffed = Write(socat_opts.sniffright, buff, bytes)) < bytes) { if ((sniffed = Write(sniffright, buff, bytes)) < bytes) {
if (sniffed < 0) if (sniffed < 0)
Warn3("-R: write(%d, buff, "F_Zu"): %s", Warn3("-R: write(%d, buff, "F_Zu"): %s",
socat_opts.sniffright, bytes, strerror(errno)); sniffright, bytes, strerror(errno));
else if (sniffed < bytes) else if (sniffed < bytes)
Warn3("-R: write(%d, buff, "F_Zu") -> "F_Zd, Warn3("-R: write(%d, buff, "F_Zu") -> "F_Zd,
socat_opts.sniffright, bytes, sniffed); sniffright, bytes, sniffed);
} }
} }

View file

@ -941,3 +941,208 @@ double Strtod(const char *nptr, char **endptr, const char *txt) {
} }
return res; return res;
} }
/* This function gets a string with possible references to environment
variables and expands them. Variables must be prefixed with '$' and their
names consist only of A-Z, a-z, 0-9, and '_'.
The name may be enclosed with { }.
To pass a literal "$" us "\$".
There are special variables supported whose values do not comr from
environment:
$$ expands to the process's pid
$PROGNAME expands to the executables basename or the value of option -lp
$TIMESTAMP expands to the actual time in format %Y%m%dT%H%M%S
$MICROS expands to the actual microseconds (decimal)
The dst output is a copy of src but with the variables expanded.
Returns 0 on success;
returns -1 when the output buffer was too short (overflow);
returns 1 on syntax error.
*/
int expandenv(
char *dst, /* prealloc'd output buff, will be \0 termd */
const char *src, /* input string to generate expansion from */
size_t n, /* length of dst */
struct timeval *tv) /* in/out timestamp for sync */
{
char c; /* char currently being lex'd/parsed */
bool esc = false; /* just got '\' */
bool bra = false; /* within ${ } */
char *nam = NULL; /* points to temp.allocated rw copy of src */
const char *e; /* pointer to isolated var name in tmp[] */
size_t s=0, d=0; /* counters in src, dst */
size_t v; /* variable name begin */
bool ofl = false; /* dst overflow, output truncated */
char tmp[18]; /* buffer for timestamp, micros */
while (c = src[s++]) {
if (esc) {
if (c == '\0') {
if (d+2 > n) { ofl = true; break; }
dst[d++] = '\\';
dst[d++] = c;
break;
}
if (c != '$') {
if (d+3 > n) { ofl = true; break; }
dst[d++] = '\\';
} else {
if (d+2 > n) { ofl = true; break; }
}
dst[d++] = c;
esc = false;
continue;
}
if (c == '\0') {
dst[d++] = c;
break;
}
if (c == '\\') {
esc = true;
continue;
}
if (c != '$') {
if (d+2 > n) { ofl = true; break; }
dst[d++] = c;
continue;
}
/* c == '$': Expecting env var to expand */
c = src[s];
if (c == '\0') {
if (d+2 > n) { ofl = true; break; }
++s;
dst[d++] = '$';
break;
}
if (c == '$') {
int wr;
/* Special case: pid */
++s;
wr = snprintf(&dst[d], n-d, F_pid, getpid());
if (wr >= n-d || wr < 0) { ofl = true; break; }
d += wr;
continue;
}
if (c == '{') {
++s;
bra = true;
}
if (!isalpha(c) && c != '_') {
/* Special case no var name, just keep '$' */
if (d+3 > n) { ofl = true; break; }
++s;
dst[d++] = '$';
dst[d++] = c;
continue;
}
v = 0; /* seems we found valid variable name */
if (nam == NULL) {
nam = strdup(src+s);
if (nam == NULL) {
errno = ENOMEM;
return -1;
}
}
while (c = src[s]) {
if (!isalnum(c) && c != '_') {
break;
}
nam[v++] = c;
++s;
}
if (bra && c != '}') {
return 1;
}
nam[v] = '\0';
/* Var name is complete */
/* Check hardcoded var names */
if (strcmp(nam, "PROGNAME") == 0) {
e = diag_get_string('p');
} else if (strcmp(nam, "TIMESTAMP") == 0) {
if (tv->tv_sec == 0) {
gettimeofday(tv, NULL);
}
struct tm tm;
localtime_r(&tv->tv_sec, &tm);
strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S", &tm);
e = tmp;
} else if (strcmp(nam, "MICROS") == 0) {
if (tv->tv_sec == 0) {
gettimeofday(tv, NULL);
}
sprintf(tmp, F_tv_usec, tv->tv_usec);
e = tmp;
} else {
e = getenv(nam);
}
if (e == NULL) {
/* Var not found, skip it */
continue;
}
/* Var found, copy it to output buffer */
if (d+strlen(e)+1 > n) { ofl = true; break; }
strcpy(&dst[d], e);
d += strlen(&dst[d]);
continue;
}
if (nam != NULL) {
free(nam);
}
if (ofl) {
dst[d] = '\0';
return -1;
}
dst[d] = '\0';
if (src[s-1] != '\0') {
errno = EINVAL;
return -1;
}
return 0;
}
int xio_opensnifffile(
const char *a,
struct timeval *tv)
{
char path[PATH_MAX];
int flags;
int rc;
int fd;
rc = expandenv(path, a, sizeof(path), tv);
if (rc < 0) {
Error2("expandenv(source=\"%s\", n="F_Zu"): Out of memory", a, sizeof(path));
errno = ENOMEM;
return -1;
} else if (rc > 0) {
Error1("expandenv(source=\"%s\"): Syntax error", a);
errno = EINVAL;
return -1;
}
flags = O_CREAT|O_WRONLY|O_APPEND|
#ifdef O_LARGEFILE
O_LARGEFILE|
#endif
#ifdef O_CLOEXEC
O_CLOEXEC|
#endif
O_NONBLOCK;
if ((fd = Open(path, flags, 0664)) < 0) {
if (errno == ENXIO) {
/* try to open pipe rdwr */
if ((fd = Open(path, flags, 0664)) < 0) {
return -1;
}
}
}
#ifdef O_CLOEXEC
if (Fcntl_l(fd, F_SETFD, FD_CLOEXEC) < 0) {
Warn2("fcntl(%d, F_SETFD, FD_CLOEXEC): %s", fd, strerror(errno));
}
#endif
return fd;
}

View file

@ -113,5 +113,6 @@ extern int xiosetenvushort(const char *varname, unsigned short value,
extern unsigned long int Strtoul(const char *nptr, char **endptr, int base, const char *txt); extern unsigned long int Strtoul(const char *nptr, char **endptr, int base, const char *txt);
extern long long int Strtoll(const char *nptr, char **endptr, int base, const char *txt); extern long long int Strtoll(const char *nptr, char **endptr, int base, const char *txt);
extern double Strtod(const char *nptr, char **endptr, const char *txt); extern double Strtod(const char *nptr, char **endptr, const char *txt);
extern int xio_opensnifffile(const char *a, struct timeval *tv);
#endif /* !defined(__sysutils_h_included) */ #endif /* !defined(__sysutils_h_included) */

98
test.sh
View file

@ -15224,6 +15224,7 @@ wait
fi ;; # NUMCOND, feats fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
N=$((N+1))
# Test the so-rcvtimeo address option with DTLS # Test the so-rcvtimeo address option with DTLS
@ -15297,6 +15298,103 @@ esac
N=$((N+1)) N=$((N+1))
# Test the use of interesting variables in the sniffing file names
NAME=VARS_IN_SNIFFPATH
case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%$NAME%*)
TEST="$NAME: sniff file names with variables"
# Start a server process with option fork that sniffs traffic and stores it in
# two files for each child process, using PID, timestamp, microseconds, and
# client IP
# Connect two times.
# For now we say that the test succeeded when 4 log files have been generated.
if ! eval $NUMCOND; then :;
elif ! A=$(testfeats IP4 TCP LISTEN PIPE STDIO); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs - TCP4 TCP4-LISTEN PIPE STDIO); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions so-reuseaddr fork) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! runsip4 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}IPv4 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
newport tcp4 # or whatever proto, or drop this line
CMD0="$TRACE $SOCAT $opts -lp server0 -r \"$td/test$N.\\\$PROGNAME-\\\$TIMESTAMP.\\\$MICROS-\\\$SERVER0_PEERADDR-\\\$\\\$.in.log\" -R \"$td/test$N.\\\$PROGNAME-\\\$TIMESTAMP.\\\$MICROS-\\\$SERVER0_PEERADDR-\\\$\\\$.out.log\" TCP4-LISTEN:$PORT,so-reuseaddr,fork PIPE"
CMD1="$TRACE $SOCAT $opts - TCP4-CONNECT:$LOCALHOST:$PORT"
printf "test $F_n $TEST... " $N
eval "$CMD0" >/dev/null 2>"${te}0" &
pid0=$!
waittcp4port $PORT 1
echo "$da" |$CMD1 >"${tf}1a" 2>"${te}1a"
rc1a=$?
echo "$da" |$CMD1 >"${tf}1b" 2>"${te}1b"
rc1b=$?
kill $(childpids $pid0) $pid0 2>/dev/null; wait
if [ $rc1a != 0 -o $rc1b != 0 ]; then
$PRINTF "$FAILED (client problem)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1a" >&2
echo "$CMD1"
cat "${te}1b" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif test $(ls -l $td/test$N.*.log |wc -l) -eq 4 &&
test $(ls $td/test$N.*.log |head -n 1 |wc -c) -ge 56; then
# Are the names correct?
# Convert timestamps to epoch and compare
# Are the contents correct?
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1a" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1b" >&2; fi
numOK=$((numOK+1))
elif test -f $td/test$N.\$PROGNAME-\$TIMESTAMP.\$MICROS-\$SERVER0_PEERADDR-\$\$.in.log; then
$PRINTF "$FAILED (vars not resolved)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1a" >&2
echo "$CMD1"
cat "${te}1b" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
else
$PRINTF "$FAILED (unknown)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1a" >&2
echo "$CMD1"
cat "${te}1b" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# end of common tests # end of common tests
################################################################################## ##################################################################################

View file

@ -89,7 +89,6 @@ int setenv(const char *name, const char *value, int overwrite) {
#endif /* !HAVE_SETENV */ #endif /* !HAVE_SETENV */
/* sanitizes an "untrusted" character. output buffer must provide at least 4 /* sanitizes an "untrusted" character. output buffer must provide at least 4
characters space. characters space.
Does not append \0. returns length of output (currently: max 4) */ Does not append \0. returns length of output (currently: max 4) */

4
xio.h
View file

@ -112,6 +112,8 @@ typedef struct {
char preferred_ip; /* preferred prot.fam. for name resolution ('0' for char preferred_ip; /* preferred prot.fam. for name resolution ('0' for
unspecified, '4', or '6') */ unspecified, '4', or '6') */
bool experimental; /* enable some features */ bool experimental; /* enable some features */
const char *sniffleft_name; /* file name with -r */
const char *sniffright_name; /* file name with -R */
} xioparms_t; } xioparms_t;
/* pack the description of a lock file */ /* pack the description of a lock file */
@ -440,6 +442,8 @@ extern int xioopenhelp(FILE *of, int level);
/* must be outside function for use by childdied handler */ /* must be outside function for use by childdied handler */
extern xiofile_t *sock1, *sock2; extern xiofile_t *sock1, *sock2;
extern int sniffleft, sniffright;
#define NUMUNKNOWN 4 #define NUMUNKNOWN 4
extern pid_t diedunknown[NUMUNKNOWN]; /* child died before it is registered */ extern pid_t diedunknown[NUMUNKNOWN]; /* child died before it is registered */
#define diedunknown1 (diedunknown[0]) #define diedunknown1 (diedunknown[0])

View file

@ -21,7 +21,9 @@ xioparms_t xioparms = {
NULL, /* syslogfac */ NULL, /* syslogfac */
'4', /* default_ip */ '4', /* default_ip */
'4', /* preferred_ip */ '4', /* preferred_ip */
false /* experimental */ false, /* experimental */
NULL, /* sniffleft_name */
NULL /* sniffright_name */
} ; } ;
@ -43,6 +45,8 @@ int xiosetopt(char what, const char *arg) {
break; break;
case 'l': xioparms.logopt = *arg; break; case 'l': xioparms.logopt = *arg; break;
case 'y': xioparms.syslogfac = arg; break; case 'y': xioparms.syslogfac = arg; break;
case 'r': xioparms.sniffleft_name = arg; break;
case 'R': xioparms.sniffright_name = arg; break;
default: default:
Error2("xiosetopt('%c', \"%s\"): unknown option", Error2("xiosetopt('%c', \"%s\"): unknown option",
what, arg?arg:"NULL"); what, arg?arg:"NULL");
@ -60,6 +64,26 @@ int xioinqopt(char what, char *arg, size_t n) {
return 0; return 0;
case 'o': return xioparms.ip4portsep; case 'o': return xioparms.ip4portsep;
case 'l': return xioparms.logopt; case 'l': return xioparms.logopt;
case 'r':
if (xioparms.sniffleft_name == NULL) {
return 1;
}
if (n < strlen(xioparms.sniffleft_name)+1) {
return -1;
}
arg[0] = '\0';
strncat(arg, xioparms.sniffleft_name, n-1);
return 0;
case 'R':
if (xioparms.sniffright_name == NULL) {
return 1;
}
if (n < strlen(xioparms.sniffright_name)+1) {
return -1;
}
arg[0] = '\0';
strncat(arg, xioparms.sniffright_name, n-1);
return 0;
default: default:
Error3("xioinqopt('%c', \"%s\", "F_Zu"): unknown option", Error3("xioinqopt('%c', \"%s\", "F_Zu"): unknown option",
what, arg, n); what, arg, n);