/* mem default */
mem = nghttp3_mem_default();
-
+ memset(&h3ssl, 0, sizeof(h3ssl));
for (;;) {
nghttp3_nv resp[10];
size_t num_nv;
#include "ossl-nghttp3.h"
#include <openssl/err.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
static int done;
static void make_nv(nghttp3_nv *nv, const char *name, const char *value)
return 0;
}
+static int try_conn(OSSL_DEMO_H3_CONN *conn, const char *bare_hostname)
+{
+ nghttp3_nv nva[16];
+ size_t num_nv = 0;
+
+ /* Build HTTP headers. */
+ make_nv(&nva[num_nv++], ":method", "GET");
+ make_nv(&nva[num_nv++], ":scheme", "https");
+ make_nv(&nva[num_nv++], ":authority", bare_hostname);
+ make_nv(&nva[num_nv++], ":path", "/");
+ make_nv(&nva[num_nv++], "user-agent", "OpenSSL-Demo/nghttp3");
+
+ /* Submit request. */
+ if (!OSSL_DEMO_H3_CONN_submit_request(conn, nva, num_nv, NULL, NULL)) {
+ ERR_raise_data(ERR_LIB_USER, ERR_R_OPERATION_FAIL,
+ "cannot submit HTTP/3 request");
+ return 0;
+ }
+
+ /* Wait for request to complete. */
+ done = 0;
+ while (!done)
+ if (!OSSL_DEMO_H3_CONN_handle_events(conn)) {
+ ERR_raise_data(ERR_LIB_USER, ERR_R_OPERATION_FAIL,
+ "cannot handle events");
+ return 0;
+ }
+ return 1;
+}
+
int main(int argc, char **argv)
{
int ret = 1;
+ int ok;
SSL_CTX *ctx = NULL;
OSSL_DEMO_H3_CONN *conn = NULL;
- nghttp3_nv nva[16];
nghttp3_callbacks callbacks = {0};
- size_t num_nv = 0;
const char *addr;
+ char *hostname, *service;
+ BIO_ADDRINFO *bai = NULL;
+ const BIO_ADDRINFO *bai_walk;
/* Check arguments. */
if (argc < 2) {
addr = argv[1];
+ hostname = NULL;
+ service = NULL;
+ if (BIO_parse_hostserv(addr, &hostname, &service, 0) != 1) {
+ fprintf(stderr, "usage: %s <host:port>\n", argv[0]);
+ goto err;
+ }
+
+ if (hostname == NULL || service == NULL) {
+ fprintf(stderr, "usage: %s <host:port>\n", argv[0]);
+ goto err;
+ }
+
+ /*
+ * Remember DNS may return more IP addresses (and it typically does these
+ * dual-stack days).
+ */
+ ok = BIO_lookup_ex(hostname, service, BIO_LOOKUP_CLIENT,
+ 0, SOCK_DGRAM, IPPROTO_UDP, &bai);
+ if (ok == 0) {
+ fprintf(stderr, "host %s not found\n", hostname);
+ goto err;
+ }
+
/* Setup SSL_CTX. */
if ((ctx = SSL_CTX_new(OSSL_QUIC_client_method())) == NULL)
goto err;
callbacks.recv_data = on_recv_data;
callbacks.end_stream = on_end_stream;
- /* Create connection. */
- if ((conn = OSSL_DEMO_H3_CONN_new_for_addr(ctx, addr, &callbacks,
- NULL, NULL)) == NULL) {
- ERR_raise_data(ERR_LIB_USER, ERR_R_OPERATION_FAIL,
- "cannot create HTTP/3 connection");
- goto err;
- }
-
- /* Build HTTP headers. */
- make_nv(&nva[num_nv++], ":method", "GET");
- make_nv(&nva[num_nv++], ":scheme", "https");
- make_nv(&nva[num_nv++], ":authority", addr);
- make_nv(&nva[num_nv++], ":path", "/");
- make_nv(&nva[num_nv++], "user-agent", "OpenSSL-Demo/nghttp3");
-
- /* Submit request. */
- if (!OSSL_DEMO_H3_CONN_submit_request(conn, nva, num_nv, NULL, NULL)) {
- ERR_raise_data(ERR_LIB_USER, ERR_R_OPERATION_FAIL,
- "cannot submit HTTP/3 request");
- goto err;
- }
-
- /* Wait for request to complete. */
- while (!done)
- if (!OSSL_DEMO_H3_CONN_handle_events(conn)) {
- ERR_raise_data(ERR_LIB_USER, ERR_R_OPERATION_FAIL,
- "cannot handle events");
- goto err;
+ /*
+ * Unlike TCP there is no handshake on UDP protocol.
+ * The BIO subsystem uses connect(2) to establish
+ * connection. However connect(2) for UDP does not
+ * perform handshake, so BIO just assumes the remote
+ * service is reachable on the first address returned
+ * by DNS. It's the SSL-handshake itself when QUIC stack
+ * realizes the service is not reachable, so the application
+ * needs to initiate a QUIC connection on the next address
+ * returned by DNS.
+ */
+ for (bai_walk = bai; bai_walk != NULL;
+ bai_walk = BIO_ADDRINFO_next(bai_walk)) {
+ conn = OSSL_DEMO_H3_CONN_new_for_addr(ctx, bai_walk, hostname,
+ &callbacks, NULL, NULL);
+ if (conn != NULL) {
+ if (try_conn(conn, addr) == 0) {
+ /*
+ * Failure, try the next address.
+ */
+ OSSL_DEMO_H3_CONN_free(conn);
+ conn = NULL;
+ ret = 1;
+ } else {
+ /*
+ * Success, done.
+ */
+ ret = 0;
+ break;
+ }
}
-
- ret = 0;
+ }
err:
if (ret != 0)
ERR_print_errors_fp(stderr);
OSSL_DEMO_H3_CONN_free(conn);
SSL_CTX_free(ctx);
+ BIO_ADDRINFO_free(bai);
return ret;
}
#include <openssl/err.h>
#include <assert.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
#define ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
enum {
return ret;
}
-OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_conn(BIO *qconn_bio,
+OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_conn(SSL *qconn,
const nghttp3_callbacks *callbacks,
const nghttp3_settings *settings,
void *user_data)
nghttp3_callbacks intl_callbacks = {0};
static const unsigned char alpn[] = {2, 'h', '3'};
- if (qconn_bio == NULL) {
+ if (qconn == NULL) {
ERR_raise_data(ERR_LIB_USER, ERR_R_PASSED_NULL_PARAMETER,
"QUIC connection BIO must be provided");
return NULL;
if ((conn = OPENSSL_zalloc(sizeof(OSSL_DEMO_H3_CONN))) == NULL)
return NULL;
- conn->qconn_bio = qconn_bio;
+ conn->qconn = qconn;
conn->user_data = user_data;
- if (BIO_get_ssl(qconn_bio, &conn->qconn) == 0) {
- ERR_raise_data(ERR_LIB_USER, ERR_R_PASSED_INVALID_ARGUMENT,
- "BIO must be an SSL BIO");
- goto err;
- }
-
/* Create the map of stream IDs to OSSL_DEMO_H3_STREAM structures. */
if ((conn->streams = lh_OSSL_DEMO_H3_STREAM_new(h3_stream_hash, h3_stream_eq)) == NULL)
goto err;
- /*
- * If the application has not started connecting yet, helpfully
- * auto-configure ALPN. If the application wants to initiate the connection
- * itself, it must take care of this itself.
- */
- if (SSL_in_before(conn->qconn))
- if (SSL_set_alpn_protos(conn->qconn, alpn, sizeof(alpn))) {
- /* SSL_set_alpn_protos returns 1 on failure */
- ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
- "failed to configure ALPN");
- goto err;
- }
-
- /*
- * We use the QUIC stack in non-blocking mode so that we can react to
- * incoming data on different streams, and e.g. incoming streams initiated
- * by a server, as and when events occur.
- */
- BIO_set_nbio(conn->qconn_bio, 1);
+ if (SSL_set_alpn_protos(conn->qconn, alpn, sizeof(alpn))) {
+ /* SSL_set_alpn_protos returns 1 on failure */
+ ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
+ "failed to configure ALPN");
+ goto err;
+ }
/*
* Disable default stream mode and create all streams explicitly. Each QUIC
return NULL;
}
-OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_addr(SSL_CTX *ctx, const char *addr,
+/*
+ * Create BIO with UDP connected socket for one of the address we got from
+ * resolver.
+ */
+static BIO *
+create_socket_bio(const BIO_ADDRINFO *bai)
+{
+ int sock;
+ BIO *bio;
+
+ sock = BIO_socket(BIO_ADDRINFO_family(bai), SOCK_DGRAM, 0, 0);
+ if (sock == -1)
+ return NULL;
+
+ if (!BIO_connect(sock, BIO_ADDRINFO_address(bai), 0))
+ goto err;
+
+ if (!BIO_socket_nbio(sock, 1))
+ goto err;
+
+ if ((bio = BIO_new(BIO_s_datagram())) == NULL)
+ goto err;
+ BIO_set_fd(bio, sock, BIO_CLOSE);
+
+ return bio;
+err:
+ BIO_closesocket(sock);
+
+ return NULL;
+}
+
+OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_addr(SSL_CTX *ctx, const BIO_ADDRINFO *bai,
+ const char *bare_hostname,
const nghttp3_callbacks *callbacks,
const nghttp3_settings *settings,
void *user_data)
BIO *qconn_bio = NULL;
SSL *qconn = NULL;
OSSL_DEMO_H3_CONN *conn = NULL;
- const char *bare_hostname;
- /* QUIC connection setup */
- if ((qconn_bio = BIO_new_ssl_connect(ctx)) == NULL)
- goto err;
-
- /* Pass the 'hostname:port' string into the ssl_connect BIO. */
- if (BIO_set_conn_hostname(qconn_bio, addr) == 0)
- goto err;
+ if ((qconn_bio = create_socket_bio(bai)) == NULL)
+ return NULL;
- /*
- * Get the 'bare' hostname out of the ssl_connect BIO. This is the hostname
- * without the port.
- */
- bare_hostname = BIO_get_conn_hostname(qconn_bio);
- if (bare_hostname == NULL)
+ qconn = SSL_new(ctx);
+ if (qconn == NULL)
goto err;
- if (BIO_get_ssl(qconn_bio, &qconn) == 0)
- goto err;
+ SSL_set_bio(qconn, qconn_bio, qconn_bio);
+ qconn_bio = NULL;
/* Set the hostname we will validate the X.509 certificate against. */
if (SSL_set1_host(qconn, bare_hostname) <= 0)
if (!SSL_set_tlsext_host_name(qconn, bare_hostname))
goto err;
- conn = OSSL_DEMO_H3_CONN_new_for_conn(qconn_bio, callbacks,
+ conn = OSSL_DEMO_H3_CONN_new_for_conn(qconn, callbacks,
settings, user_data);
if (conn == NULL)
goto err;
return conn;
err:
+ SSL_free(qconn);
BIO_free_all(qconn_bio);
return NULL;
}
-int OSSL_DEMO_H3_CONN_connect(OSSL_DEMO_H3_CONN *conn)
-{
- return SSL_connect(OSSL_DEMO_H3_CONN_get0_connection(conn));
-}
-
void *OSSL_DEMO_H3_CONN_get_user_data(const OSSL_DEMO_H3_CONN *conn)
{
return conn->user_data;
*
* Returns NULL on failure.
*/
-OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_conn(BIO *qconn_bio,
+OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_conn(SSL *qconn,
const nghttp3_callbacks *callbacks,
const nghttp3_settings *settings,
void *user_data);
* SSL_METHOD.
*/
OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_addr(SSL_CTX *ctx,
- const char *addr,
+ const BIO_ADDRINFO *addr,
+ const char *bare_hostname,
const nghttp3_callbacks *callbacks,
const nghttp3_settings *settings,
void *user_data);