From 10680c8aad36514f7cdbdc4770f5638b24631397 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Tue, 29 Dec 2020 16:45:33 +0100 Subject: [PATCH] New option setsockopt-listen using dalan --- CHANGES | 8 + dalan.c | 385 ++++++++++++++++++++++++++++++------------------- dalan.h | 2 +- doc/socat.yo | 83 +++++++---- procan-cdefs.c | 4 +- test.sh | 125 +++++++++++++--- xio-listen.c | 1 + xio-socket.c | 34 +++-- xio-socket.h | 2 + xioopts.c | 52 +++++-- xioopts.h | 4 + 11 files changed, 470 insertions(+), 230 deletions(-) diff --git a/CHANGES b/CHANGES index 919a9fc..f3d304b 100644 --- a/CHANGES +++ b/CHANGES @@ -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: diff --git a/dalan.c b/dalan.c index 99b9f2a..9239744 100644 --- a/dalan.c +++ b/dalan.c @@ -6,6 +6,7 @@ primitive subset exists. */ #include "config.h" +#include #include #include #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) { *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; } diff --git a/dalan.h b/dalan.h index d1e50ea..ce0ea01 100644 --- a/dalan.h +++ b/dalan.h @@ -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) */ diff --git a/doc/socat.yo b/doc/socat.yo index 23783b1..4511aa8 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -708,9 +708,7 @@ label(ADDRESS_SOCKET_CONNECT)dit(bf(tt(SOCKET-CONNECT:::::::::::::::< 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=::))) +label(OPTION_SETSOCKOPT)dit(bf(tt(setsockopt=::))) 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=::))) - Like tt(setsockopt-int), but 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=::))) + Like tt(setsockopt), but is a pointer to int [link(int)(TYPE_INT)] +label(OPTION_SETSOCKOPT_LISTEN)dit(bf(tt(setsockopt-listen=::))) + 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=::))) - Like tt(setsockopt-int), but must be a link(string)(TYPE_STRING). + Like tt(setsockopt), but 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 diff --git a/procan-cdefs.c b/procan-cdefs.c index 57550df..19a3fce 100644 --- a/procan-cdefs.c +++ b/procan-cdefs.c @@ -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; } diff --git a/test.sh b/test.sh index c05ef34..541a1d9 100755 --- a/test.sh +++ b/test.sh @@ -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 diff --git a/xio-listen.c b/xio-listen.c index 29c981b..be85eca 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -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); diff --git a/xio-socket.c b/xio-socket.c index 5f682bb..b891469 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -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; diff --git a/xio-socket.h b/xio-socket.h index 76b1bf0..e8f123c 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -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; diff --git a/xioopts.c b/xioopts.c index efc00ab..d9fe679 100644 --- a/xioopts.c +++ b/xioopts.c @@ -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 diff --git a/xioopts.h b/xioopts.h index 3e2127f..08056d8 100644 --- a/xioopts.h +++ b/xioopts.h @@ -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,