diff --git a/.gitignore b/.gitignore index 9c901f9fffef..4c2de3059357 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ .libs .project .settings +.idea /.vs /bld/ /build/ @@ -73,3 +74,4 @@ libstandaloneengine.a tests/string tests/config tests/ech-log/ +csnet-dist diff --git a/CMakeLists.txt b/CMakeLists.txt index c0cc0c57b8ee..705f6c0b0128 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1197,6 +1197,22 @@ if(USE_OPENSSL_QUIC) endif() endif() +option(USE_SCION "Use QUIC over SCION" OFF) +if (USE_SCION) + option(SCION_INCLUDE_DIR "Path to the SCION include directory" OFF) + option(SCION_LIBRARY_DIR "Path to the SCION lib directory" OFF) + + if (SCION_INCLUDE_DIR AND SCION_LIBRARY_DIR) + list(APPEND CURL_LIBS "scion" "nghttp2" "protobuf-c") + list(APPEND CURL_LIBDIRS ${SCION_LIBRARY_DIR}) + + include_directories(SYSTEM ${SCION_INCLUDE_DIR}) + link_directories("${SCION_LIBRARY_DIR}") + else() + message(FATAL_ERROR "If SCION is enabled both SCION_INCLUDE_DIR and SCION_LIBRARY_DIR need to be set") + endif() +endif () + if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)) message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") endif() diff --git a/README.md b/README.md index 3359818fd572..d26adb124b8a 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,94 @@ - - -# [![curl logo](https://curl.se/logo/curl-logo.svg)](https://curl.se/) - -curl is a command-line tool for transferring data specified with URL syntax. -Learn how to use curl by reading [the -manpage](https://curl.se/docs/manpage.html) or [everything -curl](https://everything.curl.dev/). - -Find out how to install curl by reading [the INSTALL -document](https://curl.se/docs/install.html). - -libcurl is the library curl is using to do its job. It is readily available to -be used by your software. Read [the libcurl -manpage](https://curl.se/libcurl/c/libcurl.html) to learn how. - -## Open Source - -curl is Open Source and is distributed under an MIT-like -[license](https://curl.se/docs/copyright.html). - -## Contact - -Contact us on a suitable [mailing list](https://curl.se/mail/) or -use GitHub [issues](https://github.com/curl/curl/issues)/ -[pull requests](https://github.com/curl/curl/pulls)/ -[discussions](https://github.com/curl/curl/discussions). - -All contributors to the project are listed in [the THANKS -document](https://curl.se/docs/thanks.html). - -## Commercial support - -For commercial support, maybe private and dedicated help with your problems or -applications using (lib)curl visit [the support page](https://curl.se/support.html). - -## Website - -Visit the [curl website](https://curl.se/) for the latest news and downloads. - -## Source code - -Download the latest source from the Git server: - - git clone https://github.com/curl/curl.git - -## Security problems - -Report suspected security problems via [our HackerOne -page](https://hackerone.com/curl) and not in public. - -## Notice - -curl contains pieces of source code that is Copyright (c) 1998, 1999 Kungliga -Tekniska Högskolan. This notice is included here to comply with the -distribution terms. - -## Backers - -Thank you to all our backers :pray: [Become a backer](https://opencollective.com/curl#section-contribute). - -## Sponsors - -Support this project by becoming a [sponsor](https://curl.se/sponsors.html). +# csnet-curl + +This a fork of [curl](https://github.com/curl/curl) that enables HTTP/3 over SCION using [csnet](https://github.com/scionproto-contrib/csnet). + +## Requirements + +1. Install a release build of csnet: [csnet - Building and Installation](https://github.com/scionproto-contrib/csnet?tab=readme-ov-file#building-and-installation) +2. Install an SSL backend that works with ngtcp2/curl (e.g, [quictls](https://github.com/quictls/openssl/tree/OpenSSL_1_1_1w+quic)) +3. Install ngtcp2 with: + ```bash + git clone --recursive https://github.com/ngtcp2/ngtcp2.git + cd ngtcp2 + cmake -DBUILD_TESTING=OFF -DENABLE_LIB_ONLY=ON -DOPENSSL_ROOT_DIR= -B cmake-build + cmake --build cmake-build + sudo cmake --install cmake-build/ + ``` +4. Install nghttp3 with: + ```bash + git clone --recursive https://github.com/ngtcp2/nghttp3 + cd nghttp3 + cmake -DENABLE_LIB_ONLY=ON -B cmake-build + cmake --build cmake-build + sudo cmake --install cmake-build/ + ``` +5. Install libpsl with: + ```bash + sudo apt install libpsl-dev + ``` +6. Install the [SCION example HTTP/3 server](https://github.com/netsec-ethz/scion-apps/pull/277) with: + ```bash + sudo apt-get install -y libpam0g-dev + git clone https://github.com/koflin/scion-apps.git + cd scion-apps + git checkout shttp3-server-example + make setup_lint + make example-shttp3-server + openssl req -x509 -newkey rsa -nodes -keyout server.key -out server.cert + ``` + +## Building + +To build the curl fork run: +```bash +cmake -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_INSTALL_RPATH=$ORIGIN/../lib \ + -DBUILD_STATIC_CURL=ON \ + -DBUILD_STATIC_LIBS=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DUSE_NGTCP2=ON \ + -DUSE_SCION=ON \ + -DSCION_INCLUDE_DIR=/include \ + -DSCION_LIBRARY_DIR=/lib \ + -DOPENSSL_ROOT_DIR= \ + -B cmake-build + +cmake --build cmake-build +``` + +## Running the Example + +1. **Start the local SCION network** + In the cloned `csnet` repository: + ```bash + sudo ./scripts/run-testnet.sh + ``` + +2. **Start the SCION example HTTP/3 server** + In the cloned `scion-apps` repository: + ```bash + sudo SCION_DAEMON_ADDRESS="127.0.0.133:30255" \ + ./bin/example-shttp3-server -cert server.cert -key server.key + ``` + > Note: The warnings `connection doesn't allow setting of receive buffer size` and `ERROR Unable to extract port from listener` are expected and can be ignored. + +3. **Custom Topology** + If you are not using the default topology, overwrite the `topology.json` file in this repository with your modified version. + +4. **Make an HTTP/3 SCION request** + + - **Using the example program** + ```bash + cmake --build cmake-build --target curl-example-http3-scion + ./cmake-build/docs/examples/http3-scion + ``` + + - **Using the command-line curl tool** + ```bash + ./cmake-build/src/curl \ + --scion-dst-ia "2-ff00:0:221" \ + --scion-topology-path "topology.json" \ + --http3-only \ + --insecure \ + https://127.0.0.132/json + ``` \ No newline at end of file diff --git a/docs/examples/CMakeLists.txt b/docs/examples/CMakeLists.txt index 595439301756..73d822589a24 100644 --- a/docs/examples/CMakeLists.txt +++ b/docs/examples/CMakeLists.txt @@ -28,6 +28,8 @@ add_custom_target(curl-examples) curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") +configure_file("../../topology.json" "${CMAKE_CURRENT_BINARY_DIR}/topology.json" COPYONLY) + foreach(_target IN LISTS check_PROGRAMS) set(_target_name "curl-example-${_target}") add_executable(${_target_name} EXCLUDE_FROM_ALL "${_target}.c") diff --git a/docs/examples/Makefile.inc b/docs/examples/Makefile.inc index 57e320c96ed9..c597f20e2214 100644 --- a/docs/examples/Makefile.inc +++ b/docs/examples/Makefile.inc @@ -60,6 +60,7 @@ check_PROGRAMS = \ http2-serverpush \ http2-upload \ http3 \ + http3-scion \ http3-present \ httpcustomheader \ httpput \ diff --git a/docs/examples/http3-scion.c b/docs/examples/http3-scion.c new file mode 100644 index 000000000000..fa2f91b3f492 --- /dev/null +++ b/docs/examples/http3-scion.c @@ -0,0 +1,68 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 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 + * + ***************************************************************************/ +/* + * Very simple HTTP/3 GET over SCION + * + */ +#include +#include + +int main(void) +{ + CURL *curl; + CURLcode res; + + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://127.0.0.132/json"); + + curl_easy_setopt(curl, CURLOPT_SCION_DST_IA, 0x2ff0000000221); + + curl_easy_setopt(curl, CURLOPT_SCION_TOPOLOGY_PATH, "topology.json"); + + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, + (long)CURL_HTTP_VERSION_3ONLY); + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 0L); + + curl_global_trace("all"); + + /* Perform the request, res gets the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } + return 0; +} \ No newline at end of file diff --git a/include/curl/curl.h b/include/curl/curl.h index 7ef5b9934987..0df00ab02a03 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2249,6 +2249,10 @@ typedef enum { /* set TLS supported signature algorithms */ CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328), + CURLOPT(CURLOPT_SCION_DST_IA, CURLOPTTYPE_LONG, 329), + + CURLOPT(CURLOPT_SCION_TOPOLOGY_PATH, CURLOPTTYPE_CBPOINT, 330), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 4187a6b2cf00..c0766c721ecd 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -90,6 +90,11 @@ #include "curl_memory.h" #include "memdebug.h" +#ifdef USE_SCION +#define setsockopt_quic(ctx,level,optname,optval,optlen) scion_setsockopt(ctx->socket,level,optname,optval,optlen) +#else +#define setsockopt_quic(ctx,level,optname,optval,optlen) setsockopt(ctx->sock,level,optname,optval,optlen) +#endif #if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) /* It makes support for IPv4-mapped IPv6 addresses. @@ -384,6 +389,47 @@ static CURLcode socket_open(struct Curl_easy *data, return CURLE_OK; } +#ifdef USE_SCION +static CURLcode socket_open_scion(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, + curl_socket_t *sockfd, struct scion_topology **topology, struct scion_network **network, struct scion_socket **socket) { + int result; + + DEBUGASSERT(addr->protocol == IPPROTO_UDP); + result = scion_topology_from_file(topology, data->set.topology_file_path); + if (result) + goto err; + + result = scion_network(network, *topology); + if (result) + + goto cleanup_topology; + + result = scion_socket(socket, addr->family, addr->socktype, SCION_PROTO_UDP, *network); + if (result) + goto cleanup_network; + + scion_getsockfd(*socket, sockfd); + +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) + if(data->conn->scope_id && (addr->family == AF_INET6)) { + struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr; + sa6->sin6_scope_id = data->conn->scope_id; + } +#endif + return CURLE_OK; + + cleanup_network: + scion_network_free(*network); + + cleanup_topology: + scion_topology_free(*topology); + + err: + /* no socket, no connection */ + return CURLE_COULDNT_CONNECT; +} +#endif + /* * Create a socket based on info from 'conn' and 'ai'. * @@ -437,6 +483,25 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn, return 0; } +#ifdef USE_SCION +static int socket_close_scion(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sock, + struct scion_socket *socket, struct scion_network *network, + struct scion_topology *topology) { + if(socket == NULL) + return 0; + + if(conn) + /* tell the multi-socket code about this */ + Curl_multi_will_close(data, sock); + + scion_close(socket); + scion_network_free(network); + scion_topology_free(topology); + + return 0; +} +#endif + /* * Close a socket. * @@ -561,6 +626,85 @@ CURLcode Curl_parse_interface(const char *input, } #ifndef CURL_DISABLE_BINDLOCAL +#ifdef USE_SCION + +static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, + struct scion_socket *socket, int af) +{ + struct Curl_sockaddr_storage sa; + struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ + curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ + struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; +#ifdef USE_IPV6 + struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; +#endif + + unsigned short port = data->set.localport; /* use this port number, 0 for + "random" */ + /* how many port numbers to try to bind to, increasing one at a time */ + int portnum = data->set.localportrange; +#ifdef IP_BIND_ADDRESS_NO_PORT + int on = 1; +#endif +#ifndef USE_IPV6 + (void)scope; +#endif + + /************************************************************* + * Select device to bind socket to + *************************************************************/ + if(!port) + /* no local kind of binding was requested */ + return CURLE_OK; + + memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); + +#ifdef USE_IPV6 + if(af == AF_INET6) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in6); + } + else +#endif + if(af == AF_INET) { + si4->sin_family = AF_INET; + si4->sin_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in); + } + +#ifdef IP_BIND_ADDRESS_NO_PORT + (void)scion_setsockopt(socket, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); +#endif + for(;;) { + if(scion_bind(socket, sock, sizeof_sa) >= 0) { + /* we succeeded to bind */ + infof(data, "Local port: %hu", port); + conn->bits.bound = TRUE; + return CURLE_OK; + } + + if(--portnum > 0) { + port++; /* try next port */ + if(port == 0) + break; + infof(data, "Bind to local port %d failed, trying next", port - 1); + /* We reuse/clobber the port variable here below */ + if(sock->sa_family == AF_INET) + si4->sin_port = ntohs(port); +#ifdef USE_IPV6 + else + si6->sin6_port = ntohs(port); +#endif + } + else + break; + } + + return CURLE_INTERFACE_FAILED; +} + +#else static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sockfd, int af, unsigned int scope) { @@ -808,6 +952,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, return CURLE_INTERFACE_FAILED; } #endif +#endif /* * verifyconnect() returns TRUE if the connect really has happened. @@ -916,6 +1061,11 @@ static CURLcode socket_connect_result(struct Curl_easy *data, struct cf_socket_ctx { int transport; struct Curl_sockaddr_ex addr; /* address to connect to */ +#ifdef USE_SCION + struct scion_topology *topology; + struct scion_network *network; + struct scion_socket *socket; +#endif curl_socket_t sock; /* current attempt socket */ struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ struct curltime started_at; /* when socket was created */ @@ -993,7 +1143,15 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock); if(ctx->sock == cf->conn->sock[cf->sockindex]) cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; +#ifdef USE_SCION + if (ctx->transport == TRNSPRT_QUIC) { + socket_close_scion(data, cf->conn, ctx->sock, ctx->socket, ctx->network, ctx->topology); + } else { + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + } +#else socket_close(data, cf->conn, !ctx->accepted, ctx->sock); +#endif ctx->sock = CURL_SOCKET_BAD; ctx->active = FALSE; memset(&ctx->started_at, 0, sizeof(ctx->started_at)); @@ -1050,12 +1208,31 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf, curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); memset(&ssloc, 0, sizeof(ssloc)); + +#ifdef USE_SCION + if (ctx->transport == TRNSPRT_QUIC) { + int result = scion_getsockname(ctx->socket, (struct sockaddr*) &ssloc, &slen, NULL); + if (result) { + failf(data, "scion_getsockname() failed with errno %d: %s", + result, scion_strerror(result)); + return CURLE_FAILED_INIT; + } + } else { + if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { + int error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + } +#else if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { int error = SOCKERRNO; failf(data, "getsockname() failed with errno %d: %s", error, Curl_strerror(error, buffer, sizeof(buffer))); return CURLE_FAILED_INIT; } +#endif if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, ctx->ip.local_ip, &ctx->ip.local_port)) { failf(data, "ssloc inet_ntop() failed with errno %d: %s", @@ -1111,7 +1288,18 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, if(!data->set.fopensocket) ctx->addr.socktype |= SOCK_NONBLOCK; #endif + +#ifdef USE_SCION + if (ctx->transport == TRNSPRT_QUIC) + { + result = socket_open_scion(data, &ctx->addr, &ctx->sock, &ctx->topology, &ctx->network, &ctx->socket); + } else + { + result = socket_open(data, &ctx->addr, &ctx->sock); + } +#else result = socket_open(data, &ctx->addr, &ctx->sock); +#endif #ifdef SOCK_NONBLOCK /* Restore the socktype after the socket is created. */ if(!data->set.fopensocket) @@ -1174,8 +1362,12 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, || ctx->addr.family == AF_INET6 #endif ) { +#ifdef USE_SCION + result = bindlocal(data, cf->conn, ctx->socket, ctx->addr.family); +#else result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, Curl_ipv6_scope(&ctx->addr.curl_sa_addr)); +#endif if(result) { if(result == CURLE_UNSUPPORTED_PROTOCOL) { /* The address family is not supported on this interface. @@ -1797,11 +1989,22 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, /* error: The 1st argument to 'connect' is -1 but should be >= 0 NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */ +#ifdef USE_SCION + rc = scion_connect(ctx->socket, &ctx->addr.curl_sa_addr, + (curl_socklen_t)ctx->addr.addrlen, data->set.ia); + if (rc != 0) { +#ifndef CURL_DISABLE_VERBOSE_STRINGS + infof(data, "Immediate connect fail for %s: %d", ctx->ip.remote_ip, rc); +#endif + return CURLE_COULDNT_CONNECT; + } +#else rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, (curl_socklen_t)ctx->addr.addrlen); if(-1 == rc) { return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); } +#endif ctx->sock_connected = TRUE; set_local_ip(cf, data); CURL_TRC_CF(data, cf, "%s socket %" FMT_SOCKET_T @@ -1820,7 +2023,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, #ifdef IP_MTU_DISCOVER case AF_INET: { int val = IP_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, + (void)setsockopt_quic(ctx, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); break; } @@ -1828,7 +2031,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, #ifdef IPV6_MTU_DISCOVER case AF_INET6: { int val = IPV6_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, + (void)setsockopt_quic(ctx, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val)); break; } @@ -1838,7 +2041,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, #if defined(UDP_GRO) && \ (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \ ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE)) - (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one, + (void)setsockopt_quic(ctx, IPPROTO_UDP, UDP_GRO, &one, (socklen_t)sizeof(one)); #endif #endif @@ -2260,3 +2463,27 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, } return CURLE_FAILED_INIT; } + +#ifdef USE_SCION +CURLcode Curl_cf_socket_peek_scion(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *psock, + const struct Curl_sockaddr_ex **paddr, + struct ip_quadruple *pip, struct scion_socket **socket) { + (void)data; + if(cf_is_socket(cf) && cf->ctx) { + struct cf_socket_ctx *ctx = cf->ctx; + + if(psock) + *psock = ctx->sock; + if(paddr) + *paddr = &ctx->addr; + if(pip) + *pip = ctx->ip; + if (socket) + *socket = ctx->socket; + return CURLE_OK; + } + return CURLE_FAILED_INIT; +} +#endif diff --git a/lib/cf-socket.h b/lib/cf-socket.h index d3e35098421e..0a0b6c0fab2d 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -167,6 +167,14 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, const struct Curl_sockaddr_ex **paddr, struct ip_quadruple *pip); +#ifdef USE_SCION +CURLcode Curl_cf_socket_peek_scion(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *psock, + const struct Curl_sockaddr_ex **paddr, + struct ip_quadruple *pip, struct scion_socket **socket); +#endif + extern struct Curl_cftype Curl_cft_tcp; extern struct Curl_cftype Curl_cft_udp; extern struct Curl_cftype Curl_cft_unix; diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index 0838ddccfba3..3ddd5f0fbf05 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -817,3 +817,5 @@ ${SIZEOF_TIME_T_CODE} /* Define to 1 if you have the SSL_set1_ech_config_list function. */ #cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST + +#cmakedefine USE_SCION 1 \ No newline at end of file diff --git a/lib/curl_setup_once.h b/lib/curl_setup_once.h index bec45de88880..6f04eaca7884 100644 --- a/lib/curl_setup_once.h +++ b/lib/curl_setup_once.h @@ -98,6 +98,10 @@ #include #endif +#ifdef USE_SCION +#include +#endif + #include "functypes.h" #ifdef __hpux diff --git a/lib/setopt.c b/lib/setopt.c index 1380c33db6fa..3d7e6b1a63d5 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1421,6 +1421,11 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, case CURLOPT_UPLOAD_FLAGS: data->set.upload_flags = (unsigned char)arg; break; +#ifdef USE_SCION + case CURLOPT_SCION_DST_IA: + data->set.ia = arg; + break; +#endif default: /* unknown option */ return CURLE_UNKNOWN_OPTION; @@ -2702,6 +2707,11 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } break; } +#endif +#ifdef USE_SCION + case CURLOPT_SCION_TOPOLOGY_PATH: + data->set.topology_file_path = ptr; + break; #endif default: return CURLE_UNKNOWN_OPTION; diff --git a/lib/urldata.h b/lib/urldata.h index 63bee65dc9d2..0cd8b9140cbf 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1553,6 +1553,10 @@ struct UserDefined { unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ unsigned char upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ +#ifdef USE_SCION + scion_ia ia; + char *topology_file_path; +#endif #ifdef HAVE_GSSAPI /* GSS-API credential delegation, see the documentation of CURLOPT_GSSAPI_DELEGATION */ diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index c534e905f6ee..ba4b5a75936d 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -2442,12 +2442,21 @@ static const struct alpn_spec ALPN_SPEC_H3 = { if(result) return result; +#ifdef USE_SCION + Curl_cf_socket_peek_scion(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL, &ctx->q.socket); +#else Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); +#endif if(!sockaddr) return CURLE_QUIC_CONNECT_ERROR; ctx->q.local_addrlen = sizeof(ctx->q.local_addr); +#ifdef USE_SCION + rv = scion_getsockname(ctx->q.socket, (struct sockaddr *)&ctx->q.local_addr, + &ctx->q.local_addrlen, NULL); +#else rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, &ctx->q.local_addrlen); +#endif if(rv == -1) return CURLE_QUIC_CONNECT_ERROR; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index b5dc44f8aa90..b0b18162a9ed 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -127,7 +127,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen, size_t gsolen, size_t *psent) { -#ifdef HAVE_SENDMSG +#if defined(HAVE_SENDMSG) || defined(USE_SCION) struct iovec msg_iov; struct msghdr msg = {0}; ssize_t sent; @@ -157,7 +157,24 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, } #endif +#ifdef USE_SCION + sent = scion_sendmsg(qctx->socket, &msg, 0, 0, NULL); + if (sent < 0) { + switch(sent) { + case SCION_ERR_WOULD_BLOCK: + return CURLE_AGAIN; + case SCION_ERR_MSG_TOO_LARGE: + printf("Drop msg too long\n"); + break; + default: + failf(data, "sendmsg() returned %zd", sent); + return CURLE_SEND_ERROR; + } + } else { + assert(pktlen == (size_t)sent); + } +#else while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == SOCKEINTR) ; @@ -189,6 +206,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, else { assert(pktlen == (size_t)sent); } +#endif #else ssize_t sent; (void)gsolen; @@ -358,7 +376,7 @@ static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg) } #endif -#ifdef HAVE_SENDMMSG +#if defined(HAVE_SENDMMSG) && !defined(USE_SCION) static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -461,7 +479,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, return result; } -#elif defined(HAVE_SENDMSG) +#elif defined(HAVE_SENDMSG) || defined(USE_SCION) static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -495,6 +513,21 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, msg.msg_namelen = sizeof(remote_addr); msg.msg_controllen = sizeof(msg_ctrl); +#ifdef USE_SCION + nread = scion_recvmsg(qctx->socket, &msg, 0, NULL, NULL); + + if (nread < 0) { + if (nread == SCION_ERR_WOULD_BLOCK) { + goto out; + } + + (void)errstr; + failf(data, "QUIC: recvmsg() unexpectedly returned %zd", + nread); + result = CURLE_RECV_ERROR; + goto out; + } +#else while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == SOCKEINTR) ; @@ -516,6 +549,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, result = CURLE_RECV_ERROR; goto out; } +#endif total_nread += (size_t)nread; @@ -549,7 +583,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, return result; } -#else /* HAVE_SENDMMSG || HAVE_SENDMSG */ +#else /* HAVE_SENDMMSG || HAVE_SENDMSG || USE_SCION */ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, @@ -606,7 +640,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, pkts, total_nread, result); return result; } -#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ +#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG && !USE_SCION */ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -615,7 +649,9 @@ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, vquic_recv_pkt_cb *recv_cb, void *userp) { CURLcode result; -#if defined(HAVE_SENDMMSG) +#if defined(USE_SCION) + result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#elif defined(HAVE_SENDMMSG) result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); #elif defined(HAVE_SENDMSG) result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index 4641c3125b01..b909613a474c 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -33,6 +33,9 @@ #define MAX_UDP_PAYLOAD_SIZE 1452 struct cf_quic_ctx { +#ifdef USE_SCION + struct scion_socket *socket; +#endif curl_socket_t sockfd; /* connected UDP socket */ struct sockaddr_storage local_addr; /* address socket is bound to */ socklen_t local_addrlen; /* length of local address */ diff --git a/src/config2setopts.c b/src/config2setopts.c index 8e4af65bae0b..522e3575b760 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -514,6 +514,13 @@ static CURLcode http_setopts(struct OperationConfig *config, my_setopt_long(curl, CURLOPT_EXPECT_100_TIMEOUT_MS, config->expect100timeout_ms); +#ifdef USE_SCION + if (config->scion_topology_path) + my_setopt_str(curl, CURLOPT_SCION_TOPOLOGY_PATH, config->scion_topology_path); + if (config->scion_dst_ia) + my_setopt_long(curl, CURLOPT_SCION_DST_IA, config->scion_dst_ia); +#endif + return CURLE_OK; } diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 975ef2a4582b..efe2c62b1a85 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -186,6 +186,10 @@ static void free_config_fields(struct OperationConfig *config) tool_safefree(config->ech); tool_safefree(config->ech_config); tool_safefree(config->ech_public); + +#ifdef USE_SCION + tool_safefree(config->scion_topology_path); +#endif } void config_free(struct OperationConfig *config) diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 62ca06ad1e52..fd071fba9704 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -241,6 +241,12 @@ struct OperationConfig { } file_clobber_mode; unsigned char upload_flags; /* Bitmask for --upload-flags */ unsigned short porttouse; + +#ifdef USE_SCION + char *scion_topology_path; + scion_ia scion_dst_ia; +#endif + BIT(remote_name_all); /* --remote-name-all */ BIT(remote_time); BIT(cookiesession); /* new session? */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 7e6a259c5fab..8a36a80e5add 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -296,6 +296,10 @@ static const struct LongShort aliases[]= { {"retry-max-time", ARG_STRG, ' ', C_RETRY_MAX_TIME}, {"sasl-authzid", ARG_STRG, ' ', C_SASL_AUTHZID}, {"sasl-ir", ARG_BOOL, ' ', C_SASL_IR}, +#ifdef USE_SCION + {"scion-dst-ia", ARG_STRG, ' ', C_SCION_DST_IA }, + {"scion-topology-path", ARG_FILE, ' ', C_SCION_TOPOLOGY_PATH }, +#endif {"service-name", ARG_STRG, ' ', C_SERVICE_NAME}, {"sessionid", ARG_BOOL|ARG_NO, ' ', C_SESSIONID}, {"show-error", ARG_BOOL, 'S', C_SHOW_ERROR}, @@ -2782,6 +2786,16 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_UPLOAD_FLAGS: /* --upload-flags */ err = parse_upload_flags(config, nextarg); break; +#ifdef USE_SCION + case C_SCION_TOPOLOGY_PATH: + err = getstr(&config->scion_topology_path, nextarg, DENY_BLANK); + break; + case C_SCION_DST_IA: { + int ret = scion_ia_parse(nextarg, strlen(nextarg), &config->scion_dst_ia); + err = ret == 0 ? PARAM_OK : PARAM_BAD_USE; + break; + } +#endif } return err; } diff --git a/src/tool_getparam.h b/src/tool_getparam.h index 50625d14410f..bcfd04b712f8 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -237,6 +237,10 @@ typedef enum { C_RETRY_MAX_TIME, C_SASL_AUTHZID, C_SASL_IR, +#ifdef USE_SCION + C_SCION_DST_IA, + C_SCION_TOPOLOGY_PATH, +#endif C_SERVICE_NAME, C_SESSIONID, C_SHOW_ERROR, diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index beb034eeaf86..80253ce3d2af 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -650,6 +650,14 @@ const struct helptxt helptext[] = { {" --sasl-ir", "Initial response in SASL authentication", CURLHELP_AUTH}, +#ifdef USE_SCION + {" --scion-dst-ia ", + "IA number of the destination AS", + CURLHELP_HTTP }, + {" --scion-topology-path ", + "Path to the SCION topology file", + CURLHELP_HTTP }, +#endif {" --service-name ", "SPNEGO service name", CURLHELP_AUTH}, diff --git a/topology.json b/topology.json new file mode 100644 index 000000000000..0bd286d6df0e --- /dev/null +++ b/topology.json @@ -0,0 +1,47 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:133", + "mtu": 1472, + "dispatched_ports": "1-65535", + "control_service": { + "cs1-ff00_0_133-1": { + "addr": "127.0.0.99:31066" + } + }, + "discovery_service": { + "cs1-ff00_0_133-1": { + "addr": "127.0.0.99:31066" + } + }, + "border_routers": { + "br1-ff00_0_133-1": { + "internal_addr": "127.0.0.97:31068", + "interfaces": { + "1": { + "underlay": { + "local": "127.0.0.29:50000", + "remote": "127.0.0.28:50000" + }, + "isd_as": "1-ff00:0:122", + "link_to": "peer", + "mtu": 1472, + "remote_interface_id": 1 + } + } + }, + "br1-ff00_0_133-2": { + "internal_addr": "127.0.0.98:31070", + "interfaces": { + "2": { + "underlay": { + "local": "127.0.0.35:50000", + "remote": "127.0.0.34:50000" + }, + "isd_as": "1-ff00:0:132", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} \ No newline at end of file