From: Stefan Eissing Date: Tue, 28 Feb 2023 09:07:21 +0000 (+0100) Subject: [PATCH] Fixing unix domain socket use in https connects. X-Git-Tag: archive/raspbian/7.88.1-10+rpi1+deb12u14^2~38 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=59078d6d234095a806204d72b38699a30e54135a;p=curl.git [PATCH] Fixing unix domain socket use in https connects. - refs #10633, when h2/h3 eyeballing was involved, unix domain socket configurations were not honoured - configuring --unix-socket will disable HTTP/3 as candidate for eyeballing - combinatino of --unix-socket and --http3-only will fail during initialisation - adding pytest test_11 to reproduce Gbp-Pq: Name fix-unix-domain-socket.patch --- diff --git a/lib/cf-http.c b/lib/cf-http.c index 2ee3d4da..a2d52ce8 100644 --- a/lib/cf-http.c +++ b/lib/cf-http.c @@ -266,7 +266,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); } else if(ctx->h21_baller.enabled) - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP); + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", + cf->conn->transport); ctx->state = CF_HC_CONNECT; /* FALLTHROUGH */ @@ -280,7 +281,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } if(time_to_start_h21(cf, data, now)) { - cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP); + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", + cf->conn->transport); } if(cf_hc_baller_is_active(&ctx->h21_baller)) { diff --git a/lib/http.c b/lib/http.c index cb585e72..2abbe67b 100644 --- a/lib/http.c +++ b/lib/http.c @@ -234,14 +234,10 @@ static CURLcode http_setup_conn(struct Curl_easy *data, Curl_mime_initpart(&http->form); data->req.p.http = http; - if((data->state.httpwant == CURL_HTTP_VERSION_3) - || (data->state.httpwant == CURL_HTTP_VERSION_3ONLY)) { + if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { CURLcode result = Curl_conn_may_http3(data, conn); if(result) return result; - - /* TODO: HTTP lower version eyeballing */ - conn->transport = TRNSPRT_QUIC; } return CURLE_OK; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 5f4f30d7..f941d624 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -363,6 +363,10 @@ bool Curl_conn_is_http3(const struct Curl_easy *data, CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn) { + if(conn->transport == TRNSPRT_UNIX) { + /* cannot do QUIC over a unix domain socket */ + return CURLE_QUIC_CONNECT_ERROR; + } if(!(conn->handler->flags & PROTOPT_SSL)) { failf(data, "HTTP/3 requested for non-HTTPS URL"); return CURLE_URL_MALFORMAT; diff --git a/tests/tests-httpd/test_11_unix.py b/tests/tests-httpd/test_11_unix.py new file mode 100644 index 00000000..8d58f498 --- /dev/null +++ b/tests/tests-httpd/test_11_unix.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 2008 - 2022, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# +import logging +import os +import socket +from threading import Thread +import pytest + +from testenv import Env, CurlClient + + +log = logging.getLogger(__name__) + +class UDSFaker: + + def __init__(self, path): + self._uds_path = path + self._done = False + + @property + def path(self): + return self._uds_path + + def start(self): + def process(self): + self._socket.listen(1) + self._process() + + try: + os.unlink(self._uds_path) + except OSError: + if os.path.exists(self._uds_path): + raise + self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._socket.bind(self._uds_path) + self._thread = Thread(target=process, daemon=True, args=[self]) + self._thread.start() + + def stop(self): + self._done = True + self._socket.close() + + def _process(self): + while self._done is False: + try: + c, client_address = self._socket.accept() + try: + data = c.recv(16) + c.sendall("""HTTP/1.1 200 Ok +Server: UdsFaker +Content-Type: application/json +Content-Length: 19 + +{ "host": "faked" }""".encode()) + finally: + c.close() + + except ConnectionAbortedError: + self._done = True + + + +@pytest.mark.skipif(condition=Env.setup_incomplete(), + reason=f"missing: {Env.incomplete_reason()}") +class TestUnix: + + @pytest.fixture(scope="class") + def uds_faker(self, env: Env) -> UDSFaker: + uds_path = os.path.join(env.gen_dir, 'uds_11.sock') + faker = UDSFaker(path=uds_path) + faker.start() + yield faker + faker.stop() + + # download http: via unix socket + def test_11_01_unix_connect_http(self, env: Env, httpd, uds_faker, repeat): + curl = CurlClient(env=env) + url = f'http://{env.domain1}:{env.http_port}/data.json' + r = curl.http_download(urls=[url], with_stats=True, + extra_args=[ + '--unix-socket', uds_faker.path, + ]) + assert r.exit_code == 0 + r.check_stats(count=1, exp_status=200) + + # download https: via unix socket + def test_11_02_unix_connect_http(self, env: Env, httpd, uds_faker, repeat): + curl = CurlClient(env=env) + url = f'https://{env.domain1}:{env.https_port}/data.json' + r = curl.http_download(urls=[url], with_stats=True, + extra_args=[ + '--unix-socket', uds_faker.path, + ]) + assert r.exit_code == 35 # CONNECT_ERROR (as faker is not TLS) + + # download HTTP/3 via unix socket + def test_11_03_unix_connect_quic(self, env: Env, httpd, uds_faker, repeat): + curl = CurlClient(env=env) + url = f'https://{env.domain1}:{env.https_port}/data.json' + r = curl.http_download(urls=[url], with_stats=True, + alpn_proto='h3', + extra_args=[ + '--unix-socket', uds_faker.path, + ]) + assert r.exit_code == 96 # QUIC CONNECT ERROR