diff --git a/CHANGES b/CHANGES index 0d25d9c..43c9cd8 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,11 @@ Features: Added option ipv6-join-source-group. Thanks to Martin Buck and David Schweizer for sending patches. + Added option http-version to PROXY-CONNECT address to support servers + that are not able to handle HTTP version 1.0 + Test: PROXY_HTTPVERSION + Feature inspired by Robin Palotai. + Corrections: 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/ diff --git a/doc/socat.yo b/doc/socat.yo index f7eb401..b2bd711 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -2421,6 +2421,10 @@ Options that can be provided with HTTP type addresses. The only HTTP address currently implemented is link(proxy-connect)(ADDRESS_PROXY_CONNECT). startdit() +label(OPTION_HTTP_VERSION)dit(bf(tt(http-version=))) + Changes the default "1.0" that is sent to the server in the initial HTTP + request. Currently it has not other effect, in particular it does not + provide any means to send a Host header. label(OPTION_PROXYPORT)dit(bf(tt(proxyport=))) Overrides the default HTTP proxy port 8080 with link()(TYPE_TCP_SERVICE). diff --git a/proxy.sh b/proxy.sh index 6c24126..9f3c53e 100755 --- a/proxy.sh +++ b/proxy.sh @@ -7,8 +7,8 @@ # accepts and answers correct HTTP CONNECT requests on stdio, and tries to # establish the connection to the given server. # it is required for socats test.sh -# for TCP, use this script as: -# socat tcp-l:8080,reuseaddr,fork exec:"proxy.sh",nofork +# for TCP, use this script like: +# socat TCP-L:8080,reuseaddr,fork EXEC:"proxy.sh",nofork # 20130622 GR allow hostnames, not only IP addresses diff --git a/proxyecho.sh b/proxyecho.sh index db7efd5..f5d0706 100755 --- a/proxyecho.sh +++ b/proxyecho.sh @@ -7,7 +7,9 @@ # accepts and answers correct HTTP CONNECT requests, but then just echoes data. # it is required for test.sh # for TCP, use this script as: -# socat tcp-l:8080,reuseaddr,crlf system:"proxyecho.sh" +# socat TCP-L:8080,reuseaddr,crlf SYSTEM:"proxyecho.sh" + +# 20230423 GR Added option -V to require particular HTTP version if type socat >/dev/null 2>&1; then SOCAT=socat @@ -24,11 +26,12 @@ HP-UX|OSF1) ;; esac -SPACES=" " +SPACES=" " REQVER=1.0 while [ -n "$1" ]; do case "$1" in -w) n="$2"; while [ "$n" -gt 0 ]; do SPACES="$SPACES "; n=$((n-1)); done shift ;; + -V) shift; REQVER="$1" ;; #-s) STAT="$2"; shift ;; esac shift @@ -36,14 +39,18 @@ done # read and parse HTTP request read l -if echo "$l" |egrep '^CONNECT +[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+ +HTTP/1.[01]$' >/dev/null +if ! echo "$l" |egrep '^CONNECT +[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+ +HTTP/[1-3].[0-9]$' >/dev/null then - : go on below -else echo "HTTP/1.0${SPACES}500 Bad Request" echo exit fi +if ! echo "$l" |egrep '^CONNECT +[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+ +HTTP/'"$REQVER"'$' >/dev/null +then + echo "HTTP/1.0${SPACES}426 Upgrade Required" + echo + exit +fi # read more headers until empty line while [ -n "$l" ]; do @@ -51,7 +58,7 @@ while [ -n "$l" ]; do done # send status -echo "HTTP/1.0${SPACES}200 OK" +echo "HTTP/$REQVER${SPACES}200 OK" # send empty line echo diff --git a/test.sh b/test.sh index 676b8dd..a07ffff 100755 --- a/test.sh +++ b/test.sh @@ -4128,27 +4128,31 @@ te="$td/test$N.stderr" tdiff="$td/test$N.diff" da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" newport tcp4 # provide free port number in $PORT -#CMD2="$TRACE $SOCAT $opts tcp-l:$PORT,crlf SYSTEM:\"read; read; $ECHO \\\"HTTP/1.0 200 OK\n\\\"; cat\"" -CMD2="$TRACE $SOCAT $opts tcp4-l:$PORT,reuseaddr,crlf exec:\"/usr/bin/env bash proxyecho.sh -w 2\"" -CMD="$TRACE $SOCAT $opts - proxy:$LOCALHOST:127.0.0.1:1000,pf=ip4,proxyport=$PORT" +#CMD2="$TRACE $SOCAT $opts TCP-L:$PORT,crlf SYSTEM:\"read; read; $ECHO \\\"HTTP/1.0 200 OK\n\\\"; cat\"" +CMD0="$TRACE $SOCAT $opts TCP4-L:$PORT,reuseaddr,crlf EXEC:\"/usr/bin/env bash proxyecho.sh -w 2\"" +CMD1="$TRACE $SOCAT $opts - PROXY:$LOCALHOST:127.0.0.1:1000,pf=ip4,proxyport=$PORT" printf "test $F_n $TEST... " $N -eval "$CMD2 2>\"${te}1\" &" +eval "$CMD0 2>\"${te}1\" &" pid=$! # background process id waittcp4port $PORT 1 -echo "$da" |$CMD >"$tf" 2>"${te}2" +echo "$da" |$CMD1 >"$tf" 2>"${te}0" if ! echo "$da" |diff - "$tf" >"$tdiff"; then $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD2 &" - echo "$CMD" - cat "${te}1" - cat "${te}2" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "diff:" cat "$tdiff" numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" else - $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi - numOK=$((numOK+1)) + $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}1" >&2; fi + numOK=$((numOK+1)) fi kill $pid 2>/dev/null wait @@ -15153,6 +15157,71 @@ fi # NUMCOND esac N=$((N+1)) + +# Test the http-version of the PROXY-CONNECT address +NAME=PROXY_HTTPVERSION +case "$TESTS" in +*%$N%*|*%functions%*|*%proxy%*|*%$NAME%*) +TEST="$NAME: PROXY-CONNECT with option http-version" +if ! eval $NUMCOND; then :; +elif ! $(type proxyecho.sh >/dev/null 2>&1); then + $PRINTF "test $F_n $TEST... ${YELLOW}proxyecho.sh not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! F=$(testfeats IP4 TCP LISTEN EXEC STDIO PROXY); then + $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! A=$(testaddrs TCP4-LISTEN EXEC STDIO PROXY-CONNECT); 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 crlf pf proxyport http-version) >/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 +ts="$td/test$N.sh" +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" +CMD0="$TRACE $SOCAT $opts TCP4-L:$PORT,reuseaddr,crlf EXEC:\"/usr/bin/env bash proxyecho.sh -V 1.1\"" +CMD1="$TRACE $SOCAT $opts - PROXY:$LOCALHOST:127.0.0.1:1000,pf=ip4,proxyport=$PORT,http-version=1.1" +printf "test $F_n $TEST... " $N +eval "$CMD0 2>\"${te}1\" &" +pid=$! # background process id +waittcp4port $PORT 1 +echo "$da" |$CMD1 >"$tf" 2>"${te}0" +if ! echo "$da" |diff - "$tf" >"$tdiff"; then + $PRINTF "$FAILED: $TRACE $SOCAT:\n" + echo "$CMD0 &" >&2 + cat "${te}0" + echo "$CMD1" >&2 + cat "${te}1" + echo "diff:" + cat "$tdiff" + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +else + $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}1" >&2; fi + numOK=$((numOK+1)) +fi +kill $pid 2>/dev/null +wait +fi ;; # NUMCOND, feats +esac +PORT=$((PORT+1)) + + # end of common tests ################################################################################## diff --git a/xio-proxy.c b/xio-proxy.c index 47d133f..941f1fe 100644 --- a/xio-proxy.c +++ b/xio-proxy.c @@ -27,6 +27,7 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, const struct optdesc opt_proxyport = { "proxyport", NULL, OPT_PROXYPORT, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC }; const struct optdesc opt_ignorecr = { "ignorecr", NULL, OPT_IGNORECR, GROUP_HTTP, PH_LATE, TYPE_BOOL, OFUNC_SPEC }; +const struct optdesc opt_http_version = { "http-version", NULL, OPT_HTTP_VERSION, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC }; const struct optdesc opt_proxy_resolve = { "proxy-resolve", "resolve", OPT_PROXY_RESOLVE, GROUP_HTTP, PH_LATE, TYPE_BOOL, OFUNC_SPEC }; const struct optdesc opt_proxy_authorization = { "proxy-authorization", "proxyauth", OPT_PROXY_AUTHORIZATION, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC }; const struct optdesc opt_proxy_authorization_file = { "proxy-authorization-file", "proxyauthfile", OPT_PROXY_AUTHORIZATION_FILE, GROUP_HTTP, PH_LATE, TYPE_STRING, OFUNC_SPEC }; @@ -239,6 +240,7 @@ int _xioopen_proxy_prepare(struct proxyvars *proxyvars, struct opt *opts, retropt_bool(opts, OPT_IGNORECR, &proxyvars->ignorecr); retropt_bool(opts, OPT_PROXY_RESOLVE, &proxyvars->doresolve); + retropt_string(opts, OPT_HTTP_VERSION, &proxyvars->version); retropt_string(opts, OPT_PROXY_AUTHORIZATION, &proxyvars->authstring); retropt_string(opts, OPT_PROXY_AUTHORIZATION_FILE, &proxyvars->authfile); @@ -331,8 +333,11 @@ int _xioopen_proxy_connect(struct single *xfd, ssize_t sresult; /* generate proxy request header - points to final target */ - rv = snprintf(request, CONNLEN, "CONNECT %s:%u HTTP/1.0\r\n", - proxyvars->targetaddr, proxyvars->targetport); + if (proxyvars->version == NULL) { + proxyvars->version = "1.0"; + } + rv = snprintf(request, CONNLEN, "CONNECT %s:%u HTTP/%s\r\n", + proxyvars->targetaddr, proxyvars->targetport, proxyvars->version); if (rv >= CONNLEN || rv < 0) { Error("_xioopen_proxy_connect(): PROXY CONNECT buffer too small"); return -1; diff --git a/xio-proxy.h b/xio-proxy.h index f74a9bc..ff8530b 100644 --- a/xio-proxy.h +++ b/xio-proxy.h @@ -8,6 +8,7 @@ struct proxyvars { bool ignorecr; + char *version; bool doresolve; char *authstring; char *authfile; @@ -17,6 +18,7 @@ struct proxyvars { extern const struct optdesc opt_proxyport; extern const struct optdesc opt_ignorecr; +extern const struct optdesc opt_http_version; extern const struct optdesc opt_proxy_resolve; extern const struct optdesc opt_proxy_authorization; extern const struct optdesc opt_proxy_authorization_file; diff --git a/xioopts.c b/xioopts.c index d93dcaf..750caa5 100644 --- a/xioopts.c +++ b/xioopts.c @@ -613,6 +613,7 @@ const struct optname optionnames[] = { #if WITH_LIBWRAP && defined(HAVE_HOSTS_DENY_TABLE) IF_IPAPP ("hosts-deny", &opt_tcpwrap_hosts_deny_table) #endif + IF_PROXY ("http-version", &opt_http_version) IF_TERMIOS("hup", &opt_hupcl) IF_TERMIOS("hupcl", &opt_hupcl) #ifdef I_POP diff --git a/xioopts.h b/xioopts.h index 084cb7b..32c3ab3 100644 --- a/xioopts.h +++ b/xioopts.h @@ -337,6 +337,7 @@ enum e_optcode { OPT_GROUP_EARLY, OPT_GROUP_LATE, OPT_HISTORY_FILE, /* readline history file */ + OPT_HTTP_VERSION, OPT_HUPCL, /* termios.c_cflag */ OPT_ICANON, /* termios.c_lflag */ OPT_ICRNL, /* termios.c_iflag */