New option setsockopt-listen using dalan

This commit is contained in:
Gerhard Rieger 2020-12-29 16:45:33 +01:00
parent 411b658939
commit 10680c8aad
11 changed files with 470 additions and 230 deletions

View file

@ -104,6 +104,14 @@ New features:
Test: GOPENUNIXSEQPACKET
Feature suggested by vi0oss.
Features:
The generic setsockopt-int and related options are, in case of
listening/accepting addresses, applied to the connected socket(s). To enable
setting options on the listening socket, a new option setsockopt-listen
has been implemented. See the documentation for info on data types.
Tests: SETSOCKOPT SETSOCKOPT_LISTEN
Thanks to Steven Danna and Korian Edeline for reporting this issue.
####################### V 1.7.3.4:
Corrections:

385
dalan.c
View file

@ -6,6 +6,7 @@
primitive subset exists. */
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#if HAVE_STDBOOL_H
@ -70,8 +71,220 @@ void dalan_init(void) {
_dalan_dflts(&dalan_opts);
}
/* read data description from line, write result to data; do not write
so much data that *p exceeds n !
/* Parses and coverts a data item.
Returns 0 on success,
-1 if the data was cut due to n limit,
1 if a syntax error occurred
2 if the actual character is white space
3 if the first character is not a type specifier
*/
static int dalan_item(int c, const char **line0, uint8_t *data, size_t *p, size_t n) {
const char *line = *line0;
size_t p1 = *p;
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n':
return 2;
case '"':
while (1) {
switch (c = *line++) {
case '\0': fputs("unterminated string\n", stderr);
*line0 = line;
return 1;
case '"':
break;
case '\\':
if (!(c = *line++)) {
fputs("continuation line not implemented\n", stderr);
*line0 = line;
return 1;
}
switch (c) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'f': c = '\f'; break;
case 'b': c = '\b'; break;
case 'a': c = '\a'; break;
#if 0
case 'e': c = '\e'; break;
#else
case 'e': c = '\033'; break;
#endif
case '0': c = '\0'; break;
}
/* PASSTHROUGH */
default:
if (p1 >= n) {
*p = p1;
*line0 = line;
return -1;
}
data[p1++] = c;
continue;
}
if (c == '"')
break;
}
break;
case '\'':
switch (c = *line++) {
case '\0': fputs("unterminated character\n", stderr);
*line0 = line;
return 1;
case '\'': fputs("error in character\n", stderr);
*line0 = line;
return 1;
case '\\':
if (!(c = *line++)) {
fputs("continuation line not implemented\n", stderr);
*line0 = line;
return 1;
}
switch (c) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'f': c = '\f'; break;
case 'b': c = '\b'; break;
case 'a': c = '\a'; break;
#if 0
case 'e': c = '\e'; break;
#else
case 'e': c = '\033'; break;
#endif
}
/* PASSTHROUGH */
default:
if (p1 >= n) { *p = p1; return -1; }
data[p1++] = c;
break;
}
if (*line != '\'') {
fputs("error in character termination\n", stderr);
*p = p1;
*line0 = line;
return 1;
}
++line;
break;
case 'x':
/* expecting hex data, must be an even number of digits!! */
while (true) {
int x;
c = *line;
if (isdigit(c&0xff)) {
x = (c-'0') << 4;
} else if (isxdigit(c&0xff)) {
x = ((c&0x07) + 9) << 4;
} else
break;
++line;
c = *line;
if (isdigit(c&0xff)) {
x |= (c-'0');
} else if (isxdigit(c&0xff)) {
x |= (c&0x07) + 9;
} else {
fputs("odd number of hexadecimal digits\n", stderr);
*p = p1;
*line0 = line;
return 1;
}
++line;
if (p1 >= n) {
*p = p1;
*line0 = line;
return -1;
}
data[p1++] = x;
}
break;
case 'l':
/* expecting decimal number, with target type long */
{
char *endptr;
*(long *)&data[p1] = strtol(line, &endptr, 10);
p1 += sizeof(long);
line = endptr;
}
break;
case 'L':
/* expecting decimal number, with target type unsigned long */
{
char *endptr;
*(unsigned long *)&data[p1] = strtol(line, &endptr, 10);
p1 += sizeof(unsigned long);
line = endptr;
}
break;
case 'i':
/* expecting decimal number, with target type int */
{
char *endptr;
*(int *)&data[p1] = strtol(line, &endptr, 10);
p1 += sizeof(int);
line = endptr;
}
break;
case 'I':
/* expecting decimal number, with target type unsigned int */
{
char *endptr;
*(unsigned int *)&data[p1] = strtol(line, &endptr, 10);
p1 += sizeof(unsigned int);
line = endptr;
}
break;
case 's':
/* expecting decimal number, with target type short */
{
char *endptr;
*(short *)&data[p1] = strtol(line, &endptr, 10);
p1 += sizeof(short);
line = endptr;
}
break;
case 'S':
/* expecting decimal number, with target type unsigned short */
{
char *endptr;
*(unsigned short *)&data[p1] = strtol(line, &endptr, 10);
p1 += sizeof(unsigned short);
line = endptr;
}
break;
case 'b':
/* expecting decimal number, with target type byte (int8_t) */
{
char *endptr;
*(int8_t *)&data[p1] = strtoul(line, &endptr, 10);
p1 += sizeof(uint8_t);
line = endptr;
}
case 'B':
/* expecting decimal number, with target type byte (uint8_t) */
{
char *endptr;
*(uint8_t *)&data[p1] = strtoul(line, &endptr, 10);
p1 += sizeof(uint8_t);
line = endptr;
}
break;
default:
*line0 = line;
return 3;
}
*p = p1;
*line0 = line;
return 0;
}
/* read data description from line (\0-terminated), write result to data; do
not write so much data that *p exceeds n !
p must be initialized to 0.
return 0 on success,
-1 if the data was cut due to n limit,
@ -79,151 +292,33 @@ void dalan_init(void) {
*p is a global data counter; especially it must be used when calculating
alignment. On successful return from the function *p must be actual!
*/
int dalan(const char *line, char *data, size_t *p, size_t n) {
int align, mask, i, x;
size_t p1 = *p;
char c;
int dalan(const char *line, uint8_t *data, size_t *p, size_t n, char deflt) {
size_t p0;
char c;
int rc;
/*fputs(line, stderr); fputc('\n', stderr);*/
while (c = *line++) {
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n':
break;
case ',':
align = 2;
while (*line == ',') {
align <<= 1;
++line;
}
mask = align - 1; /* create the bitmask */
i = (align - (p1 & mask)) & mask;
while (i && p1<n) data[p1++] = 0, --i;
if (i) { *p = p1; return -1; }
break;
case ';':
align = dalan_opts.c_int;
mask = align - 1;
i = (align - (p1 & mask)) & mask;
while (i && p1<n) data[p1++] = 0, --i;
if (i) { *p = p1; return -1; }
break;
case '"':
while (1) {
switch (c = *line++) {
case '\0': fputs("unterminated string\n", stderr);
return 1;
case '"':
break;
case '\\':
if (!(c = *line++)) {
fputs("continuation line not implemented\n", stderr);
return 1;
}
switch (c) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'f': c = '\f'; break;
case 'b': c = '\b'; break;
case 'a': c = '\a'; break;
#if 0
case 'e': c = '\e'; break;
#else
case 'e': c = '\033'; break;
#endif
case '0': c = '\0'; break;
}
/* PASSTHROUGH */
default:
if (p1 >= n) { *p = p1; return -1; }
data[p1++] = c;
continue;
}
if (c == '"')
break;
}
break;
case '\'':
switch (c = *line++) {
case '\0': fputs("unterminated character\n", stderr);
return 1;
case '\'': fputs("error in character\n", stderr);
return 1;
case '\\':
if (!(c = *line++)) {
fputs("continuation line not implemented\n", stderr);
return 1;
}
switch (c) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'f': c = '\f'; break;
case 'b': c = '\b'; break;
case 'a': c = '\a'; break;
#if 0
case 'e': c = '\e'; break;
#else
case 'e': c = '\033'; break;
#endif
}
/* PASSTHROUGH */
default:
if (p1 >= n) { *p = p1; return -1; }
data[p1++] = c;
break;
}
if (*line != '\'') {
fputs("error in character termination\n", stderr);
*p = p1; return 1;
}
++line;
break;
#if LATER
case '0':
while (1) {
/* assume there is a type specifier on beginning of rest of line */
c = *line++;
if (c == 'x') {
/* hexadecimal */ ;
} else if (isdigit(c&0xff)) {
/* octal */
} else {
/* it was only 0 */
if (c == '\0')
break;
p0 = *p;
rc = dalan_item(c, &line, data, p, n);
if (rc == 0) {
deflt = c; /* continue with this type as default */
} else if (rc == 2) {
/* white space */
continue;
} else if (rc == 3) {
/* no, we did not recognize c as type specifier, try default type */
--line;
rc = dalan_item(deflt, &line, data, p, n);
}
break;
#endif /* LATER */
case 'x':
/* expecting hex data, must be an even number of digits!! */
while (true) {
c = *line;
if (isdigit(c&0xff)) {
x = (c-'0') << 4;
} else if (isxdigit(c&0xff)) {
x = ((c&0x07) + 9) << 4;
} else
break;
++line;
c = *line;
if (isdigit(c&0xff)) {
x |= (c-'0');
} else if (isxdigit(c&0xff)) {
x |= (c&0x07) + 9;
} else {
fputs("odd number of hexadecimal digits\n", stderr);
*p = p1; return 1;
}
++line;
if (p1 >= n) { *p = p1; return -1; }
data[p1++] = x;
if (rc != 0) {
return rc;
}
break;
case 'A': case 'a':
case 'C': case 'c':
default: fprintf(stderr, "syntax error in \"%s\"\n", line-1);
return 1;
}
}
*p = p1; return 0;
/* rc == 0 */
n -= (*p-p0);
}
return 0;
}

View file

@ -25,6 +25,6 @@ extern struct dalan_opts_s dalan_opts;
extern void dalan_init(void);
extern struct dalan_opts_s *dalan_props(void);
extern int dalan(const char *line, char *data, size_t *p, size_t n);
extern int dalan(const char *line, uint8_t *data, size_t *p, size_t n, char deflt);
#endif /* !defined(__dalan_h_included) */

View file

@ -708,9 +708,7 @@ label(ADDRESS_SOCKET_CONNECT)dit(bf(tt(SOCKET-CONNECT:<domain>:<protocol>:<remot
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(CHILD)(GROUP_CHILD),link(RETRY)(GROUP_RETRY)nl()
Useful options:
link(bind)(OPTION_BIND),
link(setsockopt-int)(OPTION_SETSOCKOPT_INT),
link(setsockopt-bin)(OPTION_SETSOCKOPT_BIN),
link(setsockopt-string)(OPTION_SETSOCKOPT_STRING)
link(setsockopt)(OPTION_SETSOCKOPT),
nl()
See also:
link(TCP)(ADDRESS_TCP_CONNECT),
@ -733,9 +731,7 @@ label(ADDRESS_SOCKET_DATAGRAM)dit(bf(tt(SOCKET-DATAGRAM:<domain>:<type>:<protoco
Useful options:
link(bind)(OPTION_BIND),
link(range)(OPTION_RANGE),
link(setsockopt-int)(OPTION_SETSOCKOPT_INT),
link(setsockopt-bin)(OPTION_SETSOCKOPT_BIN),
link(setsockopt-string)(OPTION_SETSOCKOPT_STRING)
link(setsockopt)(OPTION_SETSOCKOPT),
nl()
See also:
link(UDP-DATAGRAM)(ADDRESS_UDP_DATAGRAM),
@ -756,9 +752,8 @@ label(ADDRESS_SOCKET_LISTEN)dit(bf(tt(SOCKET-LISTEN:<domain>:<protocol>:<local-a
link(-g)(option_g).nl()
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(LISTEN)(GROUP_LISTEN),link(RANGE)(GROUP_RANGE),link(CHILD)(GROUP_CHILD),link(RETRY)(GROUP_RETRY)nl()
Useful options:
link(setsockopt-int)(OPTION_SETSOCKOPT_INT),
link(setsockopt-bin)(OPTION_SETSOCKOPT_BIN),
link(setsockopt-string)(OPTION_SETSOCKOPT_STRING)
link(setsockopt)(OPTION_SETSOCKOPT),
link(setsockopt-listen)(OPTION_SETSOCKOPT_LISTEN),
nl()
See also:
link(TCP)(ADDRESS_TCP_LISTEN),
@ -777,9 +772,8 @@ label(ADDRESS_SOCKET_RECV)dit(bf(tt(SOCKET-RECV:<domain>:<type>:<protocol>:<loca
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(RANGE)(GROUP_RANGE)nl()
Useful options:
link(range)(OPTION_RANGE),
link(setsockopt-int)(OPTION_SETSOCKOPT_INT),
link(setsockopt-bin)(OPTION_SETSOCKOPT_BIN),
link(setsockopt-string)(OPTION_SETSOCKOPT_STRING)
link(setsockopt)(OPTION_SETSOCKOPT),
link(setsockopt-listen)(OPTION_SETSOCKOPT_LISTEN)
nl()
See also:
link(UDP-RECV)(ADDRESS_UDP_RECV),
@ -800,9 +794,8 @@ label(ADDRESS_SOCKET_RECVFROM)dit(bf(tt(SOCKET-RECVFROM:<domain>:<type>:<protoco
Useful options:
link(fork)(OPTION_FORK),
link(range)(OPTION_RANGE),
link(setsockopt-int)(OPTION_SETSOCKOPT_INT),
link(setsockopt-bin)(OPTION_SETSOCKOPT_BIN),
link(setsockopt-string)(OPTION_SETSOCKOPT_STRING)
link(setsockopt)(OPTION_SETSOCKOPT),
link(setsockopt-listen)(OPTION_SETSOCKOPT_LISTEN)
nl()
See also:
link(UDP-RECVFROM)(ADDRESS_UDP_RECVFROM),
@ -822,9 +815,8 @@ label(ADDRESS_SOCKET_SENDTO)dit(bf(tt(SOCKET-SENDTO:<domain>:<type>:<protocol>:<
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET)nl()
Useful options:
link(bind)(OPTION_BIND),
link(setsockopt-int)(OPTION_SETSOCKOPT_INT),
link(setsockopt-bin)(OPTION_SETSOCKOPT_BIN),
link(setsockopt-string)(OPTION_SETSOCKOPT_STRING)
link(setsockopt)(OPTION_SETSOCKOPT),
link(setsockopt-listen)(OPTION_SETSOCKOPT_LISTEN)
nl()
See also:
link(UDP-SENDTO)(ADDRESS_UDP_SENDTO),
@ -1921,22 +1913,24 @@ COMMENT(label(OPTION_USEIFBUFS)dit(bf(tt(useifbufs)))
label(OPTION_SO_TIMESTAMP)dit(bf(tt(so-timestamp)))
Sets the SO_TIMESTAMP socket option. This enables receiving and logging of
timestamp ancillary messages.
label(OPTION_SETSOCKOPT_INT)dit(bf(tt(setsockopt-int=<level>:<optname>:<optval>)))
label(OPTION_SETSOCKOPT)dit(bf(tt(setsockopt=<level>:<optname>:<optval>)))
Invokes tt(setsockopt()) for the socket with the given parameters. tt(level)
[link(int)(TYPE_INT)] is used as second argument to tt(setsockopt()) and
specifies the layer, e.g. SOL_TCP for TCP (6 on Linux), or SOL_SOCKET for
the socket layer (1 on Linux). tt(optname) [link(int)(TYPE_INT)] is the
third argument to tt(setsockopt()) and tells which socket option is to be
set. For the actual numbers you might have to look up the appropriate include
files of your system. The 4th tt(setsockopt()) parameter, tt(value)
[link(int)(TYPE_INT)], is passed to the function per pointer, and for the
length parameter NOEXPAND(sizeof(int)) is taken implicitely.
label(OPTION_SETSOCKOPT_BIN)dit(bf(tt(setsockopt-bin=<level>:<optname>:<optval>)))
Like tt(setsockopt-int), but <optval> must be provided in
link(dalan)(TYPE_DATA) format and specifies an arbitrary sequence of bytes;
the length parameter is automatically derived from the data.
files of your system. For the 4th and 5th tt(setsockopt()) parameters,
tt(value) [link(dalan)(TYPE_DATA)] specifies an arbitrary sequence of bytes
that are passed to the function per pointer, with the automatically derived
length parameter.
label(OPTION_SETSOCKOPT_INT)dit(bf(tt(setsockopt-int=<level>:<optname>:<optval>)))
Like tt(setsockopt), but <optval> is a pointer to int [link(int)(TYPE_INT)]
label(OPTION_SETSOCKOPT_LISTEN)dit(bf(tt(setsockopt-listen=<level>:<optname>:<optval>)))
Like tt(setsockopt), but for listen type addresses it is applied to the
listening socket instead of the connected socket.
label(OPTION_SETSOCKOPT_STRING)dit(bf(tt(setsockopt-string=<level>:<optname>:<optval>)))
Like tt(setsockopt-int), but <optval> must be a link(string)(TYPE_STRING).
Like tt(setsockopt), but <optval> is a link(string)(TYPE_STRING).
This string is passed to the function with trailing null character, and the
length parameter is automatically derived from the data.
enddit()
@ -2805,9 +2799,36 @@ label(TYPE_COMMAND_LINE)dit(command-line)
A string specifying a program name and its arguments, separated by single
spaces.
label(TYPE_DATA)dit(data)
A raw data specification following em(dalan) syntax. Currently the only
valid form is a string starting with 'x' followed by an even number of hex
digits, specifying a sequence of bytes.
This is a more general data specification. The given text string contains
information about the target data type and value. Generally a leading
character specifies the type of the following data item. In its specific
context a default data type may exist.nl()
Currently only the following specifications are implemented:nl()
description(
dit(i) A signed integer number, stored in host byte order.nl()
Example: bf(i-1000) (Integer number -1000)
dit(I) An unsigned integer number, stored in host byte order.nl()
dit(l) A signed long integer number, stored in host byte order.nl()
dit(L) An unsigned long integer number, stored in host byte order.nl()
dit(s) A signed short integer number, stored in host byte order.nl()
dit(S) An unsigned short integer number, stored in host byte order.nl()
dit(b) A signed byte (signed char).nl()
dit(B) An unsigned byte (unsigned char).nl()
dit(x) Following is an even number of hex digits, stored as sequence of
bytes.nl()
Example: bf(x7f000001) (IP address 127.0.0.1)
dit(") Following is a string that is used with the common conversions
\n \r \t \f \b \a \e \0; the string must be closed with '"'. Please note
that the quotes and backslashes need to be escaped from shell and socat()
conversion.nl()
Example: bf("Hello world!\n")
dit(') A single char, with the usual conversions. Please note that the
quotes and backslashes need to be escaped from shell and socat() conversion.
nl()
Example: bf('a')
)
Data items may be separated with white space without need to repeat the type
specifier again.
label(TYPE_DIRECTORY)dit(directory)
A string with usual unix() directory name semantics.
label(TYPE_FACILITY)dit(facility)
@ -2838,7 +2859,7 @@ label(TYPE_IPV4_ADDRESS)dit(IPv4 address)
an IPv4 address.nl()
Examples: 127.0.0.1, www.dest-unreach.org, dns2
label(TYPE_IPV6_ADDRESS)dit(IPv6 address)
An iPv6 address in hexnumbers-and-colons notation enclosed in brackets, or a
An IPv6 address in hexnumbers-and-colons notation enclosed in brackets, or a
hostname that resolves to an IPv6 address.nl()
Examples: [::1], [1234:5678:9abc:def0:1234:5678:9abc:def0],
ip6name.domain.org

View file

@ -159,6 +159,8 @@ int procan_cdefs(FILE *outfile) {
#ifdef SO_REUSEADDR
fprintf(outfile, "#define SO_REUSEADDR %d\n", SO_REUSEADDR);
#endif
#ifdef TCP_MAXSEG
fprintf(outfile, "#define TCP_MAXSEG %d\n", TCP_MAXSEG);
#endif
return 0;
}

125
test.sh
View file

@ -11121,14 +11121,90 @@ N=$((N+1))
SOL_SOCKET="$($PROCAN -c |grep "^#define[[:space:]]*SOL_SOCKET[[:space:]]" |cut -d' ' -f3)"
SO_REUSEADDR="$($PROCAN -c |grep "^#define[[:space:]]*SO_REUSEADDR[[:space:]]" |cut -d' ' -f3)"
TCP_MAXSEG="$($PROCAN -c |grep "^#define[[:space:]]*TCP_MAXSEG[[:space:]]" |cut -d' ' -f3)"
# test the generic setsockopt-int option
if false; then
# this test no longer works due to fix for options on listening sockets
NAME=SETSOCKOPT_INT
# Test the generic setsockopt option
NAME=SETSOCKOPT
case "$TESTS" in
*%$N%*|*%functions%*|*%ip4%*|*%tcp%*|*%generic%*|*%$NAME%*)
TEST="$NAME: test the setsockopt-int option"
TEST="$NAME: test the setsockopt option"
# Set the TCP_MAXSEG (MSS) option with a reasonable value, this should succeed.
# The try again with TCP_MAXSEG=1, this fails at least on Linux.
# Thus:
# process 0 provides a tcp listening,forking socket
# process 1 connects to this port using reasonably MSS, data transfer should
# succeed.
# Then,
# process 2 connects to this port using a very small MSS, this should fail
if ! eval $NUMCOND; then :;
elif [ -z "$TCP_MAXSEG" ]; then
# we use the numeric value of TCP_MAXSEG which might be system dependent
$PRINTF "test $F_n $TEST... ${YELLOW}value of TCPMAXSEG not known${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"
CMD0="$TRACE $SOCAT $opts TCP4-L:$PORT,so-reuseaddr,fork PIPE"
CMD1="$TRACE $SOCAT $opts - TCP:$LOCALHOST:$PORT,setsockopt=6:$TCP_MAXSEG:512"
CMD2="$TRACE $SOCAT $opts - TCP:$LOCALHOST:$PORT,setsockopt=6:$TCP_MAXSEG:1"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0" &
pid0=$!
waittcp4port $PORT 1
(echo "$da"; sleep 1) |$CMD1 >"${tf}1" 2>"${te}1" # this should always work
rc1=$?
usleep 1000000
(echo "$da"; sleep 1) |$CMD2 >"${tf}2" 2>"${te}2" # this should fail
rc2=$?
kill $pid0 $pid1 $pid2 2>/dev/null; wait
if ! echo "$da" |diff - "${tf}1" >"$tdiff"; then
$PRINTF "${YELLOW}phase 1 failed${NORMAL}\n"
echo "$CMD0 &"
cat ${te}0
echo "$CMD1"
cat ${te}1
cat "$tdiff"
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif [ $rc1 -ne 0 ]; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &"
cat ${te}0
echo "$CMD1"
cat ${te}1
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
elif [ $rc2 -eq 0 ]; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &"
cat ${te}0
echo "$CMD2"
cat ${te}2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
if [ -n "$debug" ]; then cat "${te}0" "${te}1" "${te}2"; fi
numOK=$((numOK+1))
fi
fi # NUMCOND, SO_REUSEADDR
;;
esac
PORT=$((PORT+1))
N=$((N+1))
# Test the generic setsockopt-listen option
# This test, with setsockopt-int, no longer worked due to fix for options on
# listening sockets
# Now it got a chance again using new option setsockopt-listen
#NAME=SETSOCKOPT_INT
NAME=SETSOCKOPT_LISTEN
case "$TESTS" in
*%$N%*|*%functions%*|*%ip4%*|*%tcp%*|*%generic%*|*%$NAME%*)
TEST="$NAME: test the setsockopt-listen option"
# there are not many socket options that apply to non global resources, do not
# require root, do not require a network connection, and can easily be
# tested. SO_REUSEADDR seems to fit:
@ -11145,50 +11221,59 @@ elif [ -z "$SO_REUSEADDR" ]; then
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tp="$PORT"
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts TCP4-L:$tp,setsockopt-int=$SOL_SOCKET:$SO_REUSEADDR:1 PIPE"
CMD1="$TRACE $SOCAT $opts - TCP:localhost:$tp"
CMD0="$TRACE $SOCAT $opts TCP4-L:$PORT,setsockopt-listen=$SOL_SOCKET:$SO_REUSEADDR:1 PIPE"
CMD1="$TRACE $SOCAT $opts - TCP:$LOCALHOST:$PORT"
CMD2="$CMD0"
CMD3="$CMD1"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0" &
pid0=$!
waittcp4port $tp 1
(echo "$da"; sleep 3) |$CMD1 >"$tf" 2>"${te}1" & # this should always work
pid1=$!
usleep 1000000
waittcp4port $PORT 1
echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" # this should always work
rc1=$?
kill $pid0 2>/dev/null; wait
$CMD2 >/dev/null 2>"${te}2" &
pid2=$!
waittcp4port $tp 1
(echo "$da") |$CMD3 >"${tf}3" 2>"${te}3"
waittcp4port $PORT 1
echo "$da" |$CMD3 >"${tf}3" 2>"${te}3"
rc3=$?
kill $pid0 $pid1 $pid2 2>/dev/null; wait
if ! echo "$da" |diff - "$tf"; then
kill $pid2 2>/dev/null; wait
if ! echo "$da" |diff - "${tf}1" >"${tdiff}1"; then
$PRINTF "${YELLOW}phase 1 failed${NORMAL}\n"
echo "$CMD0 &"
cat ${te}0
echo "$CMD1"
cat ${te}1
cat "${tdiff}1"
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif [ $rc3 -ne 0 ]; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &"
cat ${te}0
echo "$CMD1"
cat ${te}1
echo "$CMD2 &"
cat ${te}2
echo "$CMD3"
cat "${te}2" "${te}3"
cat ${te}3
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
elif ! echo "$da" |diff - "${tf}3"; then
elif ! echo "$da" |diff - "${tf}3" >"${tdiff}3"; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &"
cat ${te}0
echo "$CMD1"
cat ${te}1
echo "$CMD2 &"
cat ${te}2
echo "$CMD3"
echo "$da" |diff - "${tf}3"
cat ${te}3
cat "${tdiff}3"
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
@ -11201,8 +11286,6 @@ fi # NUMCOND, SO_REUSEADDR
esac
PORT=$((PORT+1))
N=$((N+1))
#
fi
NAME=SCTP4STREAM

View file

@ -146,6 +146,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) {
return STAT_RETRYLATER;
}
applyopts(xfd->fd, opts, PH_PASTSOCKET);
applyopts_cloexec(xfd->fd, opts);

View file

@ -186,9 +186,11 @@ const struct optdesc opt_protocol_family = { "protocol-family", "pf", OPT_PROTOC
const struct optdesc opt_protocol = { "protocol", NULL, OPT_PROTOCOL, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC };
/* generic setsockopt() options */
const struct optdesc opt_setsockopt_int = { "setsockopt-int", "sockopt-int", OPT_SETSOCKOPT_INT, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_INT, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_setsockopt_bin = { "setsockopt-bin", "sockopt-bin", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_setsockopt_string = { "setsockopt-string", "sockopt-string", OPT_SETSOCKOPT_STRING, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_STRING, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_setsockopt = { "setsockopt", "sockopt", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_setsockopt_int = { "setsockopt-int", "sockopt-int", OPT_SETSOCKOPT_INT, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_INT, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_setsockopt_bin = { "setsockopt-bin", "sockopt-bin", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_setsockopt_string = { "setsockopt-string", "sockopt-string", OPT_SETSOCKOPT_STRING, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_STRING, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_setsockopt_listen = { "setsockopt-listen", "sockopt-listen", OPT_SETSOCKOPT_LISTEN, GROUP_SOCKET,PH_PREBIND, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
const struct optdesc opt_null_eof = { "null-eof", NULL, OPT_NULL_EOF, GROUP_SOCKET, PH_INIT, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.null_eof) };
@ -239,7 +241,7 @@ int xioopen_socket_connect(int argc, const char *argv[], struct opt *opts,
themsize = 0;
if ((result =
dalan(address, (char *)&them.soa.sa_data, &themsize, sizeof(them)))
dalan(address, (uint8_t *)&them.soa.sa_data, &themsize, sizeof(them), 'i'))
< 0) {
Error1("data too long: \"%s\"", address);
} else if (result > 0) {
@ -316,7 +318,7 @@ int xioopen_socket_listen(int argc, const char *argv[], struct opt *opts,
socket_init(0, &us);
ussize = 0;
if ((result =
dalan(usname, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
dalan(usname, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i'))
< 0) {
Error1("data too long: \"%s\"", usname);
} else if (result > 0) {
@ -407,8 +409,8 @@ int _xioopen_socket_sendto(const char *pfname, const char *type,
xfd->peersa.soa.sa_family = pf;
themsize = 0;
if ((result =
dalan(address, (char *)&xfd->peersa.soa.sa_data, &themsize,
sizeof(xfd->peersa)))
dalan(address, (uint8_t *)&xfd->peersa.soa.sa_data, &themsize,
sizeof(xfd->peersa), 'i'))
< 0) {
Error1("data too long: \"%s\"", address);
} else if (result > 0) {
@ -438,7 +440,7 @@ int _xioopen_socket_sendto(const char *pfname, const char *type,
if (retropt_string(opts, OPT_BIND, &bindstring) == 0) {
ussize = 0;
if ((result =
dalan(bindstring, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
dalan(bindstring, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i'))
< 0) {
Error1("data too long: \"%s\"", bindstring);
} else if (result > 0) {
@ -504,7 +506,7 @@ int xioopen_socket_recvfrom(int argc, const char *argv[], struct opt *opts,
ussize = 0;
if ((result =
dalan(address, (char *)&us->soa.sa_data, &ussize, sizeof(*us)))
dalan(address, (uint8_t *)&us->soa.sa_data, &ussize, sizeof(*us), 'i'))
< 0) {
Error1("data too long: \"%s\"", address);
} else if (result > 0) {
@ -581,7 +583,7 @@ int xioopen_socket_recv(int argc, const char *argv[], struct opt *opts,
ussize = 0;
if ((result =
dalan(address, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
dalan(address, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i'))
< 0) {
Error1("data too long: \"%s\"", address);
} else if (result > 0) {
@ -649,8 +651,8 @@ int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts,
xfd->peersa.soa.sa_family = pf;
themsize = 0;
if ((result =
dalan(address, (char *)&xfd->peersa.soa.sa_data, &themsize,
sizeof(xfd->peersa)))
dalan(address, (uint8_t *)&xfd->peersa.soa.sa_data, &themsize,
sizeof(xfd->peersa), 'i'))
< 0) {
Error1("data too long: \"%s\"", address);
} else if (result > 0) {
@ -1992,9 +1994,9 @@ int xioparsenetwork(const char *rangename, int pf, struct xiorange *range) {
strncpy(addrname, rangename, maskname-rangename-1); /* ok */
addrname[maskname-rangename-1] = '\0';
result =
dalan(addrname, (char *)&range->netaddr.soa.sa_data, &addrlen,
dalan(addrname, (uint8_t *)&range->netaddr.soa.sa_data, &addrlen,
sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
/* data length */);
/* data length */, 'i');
if (result < 0) {
Error1("data too long: \"%s\"", addrname);
free(addrname); return STAT_NORETRY;
@ -2004,9 +2006,9 @@ int xioparsenetwork(const char *rangename, int pf, struct xiorange *range) {
}
free(addrname);
result =
dalan(maskname, (char *)&range->netmask.soa.sa_data, &masklen,
dalan(maskname, (uint8_t *)&range->netmask.soa.sa_data, &masklen,
sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
/* data length */);
/* data length */, 'i');
if (result < 0) {
Error1("data too long: \"%s\"", maskname);
return STAT_NORETRY;

View file

@ -62,9 +62,11 @@ extern const struct optdesc opt_fiosetown;
extern const struct optdesc opt_siocspgrp;
extern const struct optdesc opt_bind;
extern const struct optdesc opt_protocol_family;
extern const struct optdesc opt_setsockopt;
extern const struct optdesc opt_setsockopt_int;
extern const struct optdesc opt_setsockopt_bin;
extern const struct optdesc opt_setsockopt_string;
extern const struct optdesc opt_setsockopt_listen;
extern const struct optdesc opt_null_eof;

View file

@ -1402,8 +1402,10 @@ const struct optname optionnames[] = {
#if WITH_EXEC || WITH_SYSTEM
IF_EXEC ("setsid", &opt_setsid)
#endif
IF_SOCKET ("setsockopt", &opt_setsockopt)
IF_SOCKET ("setsockopt-bin", &opt_setsockopt_bin)
IF_SOCKET ("setsockopt-int", &opt_setsockopt_int)
IF_SOCKET ("setsockopt-listen", &opt_setsockopt_listen)
IF_SOCKET ("setsockopt-string", &opt_setsockopt_string)
IF_ANY ("setuid", &opt_setuid)
IF_ANY ("setuid-early", &opt_setuid_early)
@ -1517,8 +1519,10 @@ const struct optname optionnames[] = {
#ifdef SO_USELOOPBACK /* AIX433, Solaris */
IF_SOCKET ("so-useloopback", &opt_so_useloopback)
#endif /* SO_USELOOPBACK */
IF_SOCKET ("sockopt", &opt_setsockopt)
IF_SOCKET ("sockopt-bin", &opt_setsockopt_bin)
IF_SOCKET ("sockopt-int", &opt_setsockopt_int)
IF_SOCKET ("sockopt-listen", &opt_setsockopt_listen)
IF_SOCKET ("sockopt-string", &opt_setsockopt_string)
IF_SOCKS4 ("socksport", &opt_socksport)
IF_SOCKS4 ("socksuser", &opt_socksuser)
@ -1824,7 +1828,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
char token[2048], *tokp; size_t len;
int parsres;
int result;
char optbuf[256]; size_t optlen;
uint8_t optbuf[256]; size_t optlen;
const char *endkey[6+1];
const char *endval[5+1];
const char *assign_str = "=";
@ -1895,15 +1899,15 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
ent = (struct optname *)
keyw((struct wordent *)optionnames, token, optionnum);
if (ent == NULL) {
Error1("parseopts(): unknown option \"%s\"", token);
Error1("parseopts_table(): unknown option \"%s\"", token);
continue;
}
if (!(ent->desc->group & groups) && !(ent->desc->group & GROUP_ANY) &&
!xioopts_ignoregroups) {
Error1("parseopts(): option \"%s\" not supported with this address type",
Error1("parseopts_table(): option \"%s\" not supported with this address type",
token /*a0*/);
Info2("parseopts() groups=%08x, ent->group=%08x",
Info2("parseopts_table() groups=%08x, ent->group=%08x",
groups, ent->desc->group);
#if 0
continue;
@ -1946,8 +1950,8 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
if (!assign) { Error1("option \"%s\": value required", a0);
continue; }
optlen = 0;
if ((result = dalan(token, optbuf, &optlen, sizeof(optbuf))) != 0) {
Error1("parseopts(): problem with \"%s\" data", token);
if ((result = dalan(token, optbuf, &optlen, sizeof(optbuf), 'i')) != 0) {
Error1("parseopts_table(): problem with \"%s\" data", token);
continue;
}
if (((*opts)[i].value.u_bin.b_data = memdup(optbuf, optlen)) == NULL) {
@ -1962,7 +1966,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
char *rest;
ul = strtoul(token, &rest/*!*/, 0);
if (ul > UCHAR_MAX) {
Error3("parseopts(%s): byte value exceeds limit (%lu vs. %u), using max",
Error3("parseopts_table(%s): byte value exceeds limit (%lu vs. %u), using max",
a0, ul, UCHAR_MAX);
(*opts)[i].value.u_byte = UCHAR_MAX;
} else {
@ -2011,7 +2015,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
char *rest;
ulongval = strtoul(token, &rest/*!*/, 0);
if (ulongval > UINT_MAX) {
Error3("parseopts(%s): unsigned int value exceeds limit (%lu vs. %u), using max",
Error3("parseopts_table(%s): unsigned int value exceeds limit (%lu vs. %u), using max",
a0, ulongval, UINT_MAX);
}
(*opts)[i].value.u_uint = ulongval;
@ -2030,7 +2034,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
char *rest;
ulongval = strtoul(token, &rest/*!*/, 0);
if (ulongval > USHRT_MAX) {
Error3("parseopts(%s): unsigned short value exceeds limit (%lu vs. %u), using max",
Error3("parseopts_table(%s): unsigned short value exceeds limit (%lu vs. %u), using max",
a0, ulongval, USHRT_MAX);
}
(*opts)[i].value.u_ushort = ulongval;
@ -2266,8 +2270,8 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
}
++rest;
optlen = 0;
if ((result = dalan(rest, optbuf, &optlen, sizeof(optbuf))) != 0) {
Error1("parseopts(): problem with \"%s\" data", rest);
if ((result = dalan(rest, optbuf, &optlen, sizeof(optbuf), 'i')) != 0) {
Error1("parseopts_table(): problem with \"%s\" data", rest);
continue;
}
if (((*opts)[i].value2.u_bin.b_data = memdup(optbuf, optlen)) == NULL) {
@ -2327,6 +2331,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
break;
case TYPE_INT_INT_BIN:
case TYPE_INT_INT_GENERIC:
if (!assign) {
Error1("option \"%s\": values required", a0);
continue;
@ -2346,8 +2351,8 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
}
++rest;
optlen = 0;
if ((result = dalan(rest, optbuf, &optlen, sizeof(optbuf))) != 0) {
Error1("parseopts(): problem with \"%s\" data", rest);
if ((result = dalan(rest, optbuf, &optlen, sizeof(optbuf), 'i')) != 0) {
Error1("parseopts_table(): problem with \"%s\" data", rest);
continue;
}
if (((*opts)[i].value3.u_bin.b_data = memdup(optbuf, optlen)) == NULL) {
@ -2494,8 +2499,25 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
break;
#endif /* defined(WITH_IP4) */
case TYPE_GENERIC:
if (!assign) {
(*opts)[i].value.u_int = 1;
} else {
int rc;
size_t binlen = 64; /*!!!*/
if (((*opts[i]).value.u_bin.b_data = Malloc(binlen)) == NULL) Error("!!!");
(*opts)[i].value.u_bin.b_len = 0;
rc = dalan(token, (*opts)[i].value.u_bin.b_data,
&(*opts)[i].value.u_bin.b_len, binlen, 'i');
if (rc != 0) {
Error("!!!");
}
//(*opts)[i].value.u_bin.b_len
}
break;
default:
Error2("parseopts(): internal error on option \"%s\": unimplemented type %d",
Error2("parseopts_table(): internal error on option \"%s\": unimplemented type %d",
ent->desc->defname, ent->desc->type);
continue;
}
@ -2964,7 +2986,7 @@ int retropt_bind(struct opt *opts,
case AF_UNSPEC:
{
size_t p = 0;
dalan(bindname, (char *)sa->sa_data, &p, *salen-sizeof(sa->sa_family));
dalan(bindname, (uint8_t *)sa->sa_data, &p, *salen-sizeof(sa->sa_family), 'i');
*salen = p + sizeof(sa->sa_family);
*salen = p +
#if HAVE_STRUCT_SOCKADDR_SALEN

View file

@ -64,6 +64,7 @@ enum e_types {
TYPE_INT_INT_INT, /* 3 params: first and second are int, 3rd is int */
TYPE_INT_INT_BIN, /* 3 params: first and second are int, 3rd is binary */
TYPE_INT_INT_STRING, /* 3 params: first and second are int, 3rd is string */
TYPE_INT_INT_GENERIC, /* 3 params: first and second are int, 3rd is specified by value (dalan syntax) */
TYPE_IP4NAME, /* IPv4 hostname or address */
#if HAVE_STRUCT_LINGER
@ -72,6 +73,8 @@ enum e_types {
#if HAVE_STRUCT_IP_MREQ || HAVE_STRUCT_IP_MREQN
TYPE_IP_MREQN, /* for struct ip_mreq or struct ip_mreqn */
#endif
TYPE_GENERIC, /* type is determined from (text) data provided */
} ;
enum e_func {
@ -596,6 +599,7 @@ enum e_optcode {
OPT_SETSID,
OPT_SETSOCKOPT_BIN,
OPT_SETSOCKOPT_INT,
OPT_SETSOCKOPT_LISTEN,
OPT_SETSOCKOPT_STRING,
OPT_SETUID,
OPT_SETUID_EARLY,