Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
.libs
.project
.settings
.idea
/.vs
/bld/
/build/
Expand Down
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,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()
Expand Down
2 changes: 2 additions & 0 deletions docs/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions docs/examples/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ check_PROGRAMS = \
http2-serverpush \
http2-upload \
http3 \
http3-scion \
http3-present \
httpcustomheader \
httpput \
Expand Down
68 changes: 68 additions & 0 deletions docs/examples/http3-scion.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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
*
***************************************************************************/
/* <DESC>
* Very simple HTTP/3 GET over SCION
* </DESC>
*/
#include <stdio.h>
#include <curl/curl.h>

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;
}
4 changes: 4 additions & 0 deletions include/curl/curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2261,6 +2261,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;

Expand Down
158 changes: 153 additions & 5 deletions lib/cf-socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,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.
Expand Down Expand Up @@ -345,6 +350,46 @@ CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be if(result), right? Everywhere in this file.

goto err;

result = scion_network(network, *topology);
if (result)
goto cleanup_topology;

result = scion_socket(socket, addr->family, 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

static CURLcode socket_open(struct Curl_easy *data,
struct Curl_sockaddr_ex *addr,
curl_socket_t *sockfd)
Expand Down Expand Up @@ -414,6 +459,31 @@ CURLcode Curl_socket_open(struct Curl_easy *data,
return socket_open(data, addr, sockfd);
}

#ifdef USE_SCION
static int socket_close_scion(struct Curl_easy *data, struct connectdata *conn,
int use_callback, curl_socket_t sock, struct scion_socket *socket) {
if(socket == NULL)
return 0;

if(use_callback && conn && conn->fclosesocket) {
int rc;
Curl_multi_will_close(data, sock);
Curl_set_in_callback(data, TRUE);
rc = conn->fclosesocket(conn->closesocket_client, sock);
Curl_set_in_callback(data, FALSE);
return rc;
}

if(conn)
/* tell the multi-socket code about this */
Curl_multi_will_close(data, sock);

scion_close(socket);

return 0;
}
#endif

static int socket_close(struct Curl_easy *data, struct connectdata *conn,
int use_callback, curl_socket_t sock)
{
Expand Down Expand Up @@ -561,7 +631,7 @@ CURLcode Curl_parse_interface(const char *input,
return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}

#ifndef CURL_DISABLE_BINDLOCAL
#if !defined(CURL_DISABLE_BINDLOCAL) && !defined(USE_SCION)
static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t sockfd, int af, unsigned int scope)
{
Expand Down Expand Up @@ -917,6 +987,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 */
Expand Down Expand Up @@ -994,7 +1069,17 @@ 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->accepted, ctx->sock, ctx->socket);
scion_network_free(ctx->network);
scion_topology_free(ctx->topology);
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do network and topology cleanup in socket_close_scion

} 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;
if(ctx->active && cf->sockindex == FIRSTSOCKET)
cf->conn->remote_addr = NULL;
Expand Down Expand Up @@ -1053,12 +1138,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",
Expand Down Expand Up @@ -1114,7 +1218,17 @@ 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)
Expand Down Expand Up @@ -1170,7 +1284,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
}
}

#ifndef CURL_DISABLE_BINDLOCAL
#if !defined(CURL_DISABLE_BINDLOCAL) && !defined(USE_SCION)
/* possibly bind the local end to an IP, interface or port */
if(ctx->addr.family == AF_INET
#ifdef USE_IPV6
Expand Down Expand Up @@ -1823,11 +1937,21 @@ 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) {
printf("scion connect failed: %d\n", rc);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scion > SCION

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
Expand All @@ -1846,15 +1970,15 @@ 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;
}
#endif
#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;
}
Expand All @@ -1864,7 +1988,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
Expand Down Expand Up @@ -2269,6 +2393,30 @@ static bool cf_is_socket(struct Curl_cfilter *cf)
cf->cft == &Curl_cft_tcp_accept);
}

#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

CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *psock,
Expand Down
Loading