1 /* 2 * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 #include <openssl/err.h> 10 #include <openssl/ssl.h> 11 #include <openssl/quic.h> 12 #ifdef _WIN32 /* Windows */ 13 # include <winsock2.h> 14 #else /* Linux/Unix */ 15 # include <netinet/in.h> 16 # include <unistd.h> 17 #endif 18 #include <assert.h> 19 20 /* 21 * This is a basic demo of QUIC server functionality in which one connection at 22 * a time is accepted in a blocking loop. 23 */ 24 25 /* ALPN string for TLS handshake */ 26 static const unsigned char alpn_ossltest[] = { 27 /* "\x08ossltest" (hex for EBCDIC resilience) */ 28 0x08, 0x6f, 0x73, 0x73, 0x6c, 0x74, 0x65, 0x73, 0x74 29 }; 30 31 /* This callback validates and negotiates the desired ALPN on the server side. */ 32 static int select_alpn(SSL *ssl, 33 const unsigned char **out, unsigned char *out_len, 34 const unsigned char *in, unsigned int in_len, 35 void *arg) 36 { 37 if (SSL_select_next_proto((unsigned char **)out, out_len, 38 alpn_ossltest, sizeof(alpn_ossltest), in, in_len) 39 != OPENSSL_NPN_NEGOTIATED) 40 return SSL_TLSEXT_ERR_ALERT_FATAL; 41 42 return SSL_TLSEXT_ERR_OK; 43 } 44 45 /* Create SSL_CTX. */ 46 static SSL_CTX *create_ctx(const char *cert_path, const char *key_path) 47 { 48 SSL_CTX *ctx; 49 50 ctx = SSL_CTX_new(OSSL_QUIC_server_method()); 51 if (ctx == NULL) 52 goto err; 53 54 /* Load certificate and corresponding private key. */ 55 if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) <= 0) { 56 fprintf(stderr, "couldn't load certificate file: %s\n", cert_path); 57 goto err; 58 } 59 60 if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0) { 61 fprintf(stderr, "couldn't load key file: %s\n", key_path); 62 goto err; 63 } 64 65 if (!SSL_CTX_check_private_key(ctx)) { 66 fprintf(stderr, "private key check failed\n"); 67 goto err; 68 } 69 70 /* Setup ALPN negotiation callback. */ 71 SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); 72 return ctx; 73 74 err: 75 SSL_CTX_free(ctx); 76 return NULL; 77 } 78 79 /* Create UDP socket using given port. */ 80 static int create_socket(uint16_t port) 81 { 82 int fd = -1; 83 struct sockaddr_in sa = {0}; 84 85 if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 86 fprintf(stderr, "cannot create socket"); 87 goto err; 88 } 89 90 sa.sin_family = AF_INET; 91 sa.sin_port = htons(port); 92 93 if (bind(fd, (const struct sockaddr *)&sa, sizeof(sa)) < 0) { 94 fprintf(stderr, "cannot bind to %u\n", port); 95 goto err; 96 } 97 98 return fd; 99 100 err: 101 if (fd >= 0) 102 BIO_closesocket(fd); 103 104 return -1; 105 } 106 107 /* Main loop for servicing a single incoming QUIC connection. */ 108 static int run_quic_conn(SSL *conn) 109 { 110 size_t written = 0; 111 112 fprintf(stderr, "=> Received connection\n"); 113 114 /* 115 * Write the message "hello" on the connection using a default stream 116 * and then immediately conclude the stream (end-of-stream). This 117 * demonstrates the use of SSL_write_ex2 for optimised FIN generation. 118 * 119 * Since we inherit our blocking mode from the parent QUIC SSL object (the 120 * listener) by default, this call is also blocking. 121 */ 122 if (!SSL_write_ex2(conn, "hello\n", 6, SSL_WRITE_FLAG_CONCLUDE, &written) 123 || written != 6) { 124 fprintf(stderr, "couldn't write on connection\n"); 125 ERR_print_errors_fp(stderr); 126 return 0; 127 } 128 129 /* Shut down the connection (blocking). */ 130 if (SSL_shutdown(conn) != 1) { 131 ERR_print_errors_fp(stderr); 132 return 0; 133 } 134 135 fprintf(stderr, "=> Finished with connection\n"); 136 return 1; 137 } 138 139 /* Main loop for server to accept QUIC connections. */ 140 static int run_quic_server(SSL_CTX *ctx, int fd) 141 { 142 int ok = 0; 143 SSL *listener = NULL, *conn = NULL; 144 145 /* Create a new QUIC listener. */ 146 if ((listener = SSL_new_listener(ctx, 0)) == NULL) 147 goto err; 148 149 /* Provide the listener with our UDP socket. */ 150 if (!SSL_set_fd(listener, fd)) 151 goto err; 152 153 /* Begin listening. */ 154 if (!SSL_listen(listener)) 155 goto err; 156 157 /* 158 * Listeners, and other QUIC objects, default to operating in blocking mode, 159 * so the below call is not actually necessary. The configured behaviour is 160 * inherited by child objects. 161 */ 162 if (!SSL_set_blocking_mode(listener, 1)) 163 goto err; 164 165 for (;;) { 166 /* Blocking wait for an incoming connection, similar to accept(2). */ 167 conn = SSL_accept_connection(listener, 0); 168 if (conn == NULL) { 169 fprintf(stderr, "error while accepting connection\n"); 170 goto err; 171 } 172 173 /* 174 * Optionally, we could disable blocking mode on the accepted connection 175 * here by calling SSL_set_blocking_mode(). 176 */ 177 178 /* 179 * Service the connection. In a real application this would be done 180 * concurrently. In this demonstration program a single connection is 181 * accepted and serviced at a time. 182 */ 183 if (!run_quic_conn(conn)) { 184 SSL_free(conn); 185 goto err; 186 } 187 188 /* Free the connection, then loop again, accepting another connection. */ 189 SSL_free(conn); 190 } 191 192 ok = 1; 193 err: 194 if (!ok) 195 ERR_print_errors_fp(stderr); 196 197 SSL_free(listener); 198 return ok; 199 } 200 201 int main(int argc, char **argv) 202 { 203 int rc = 1; 204 SSL_CTX *ctx = NULL; 205 int fd = -1; 206 unsigned long port; 207 208 if (argc < 4) { 209 fprintf(stderr, "usage: %s <port> <server.crt> <server.key>\n", argv[0]); 210 goto err; 211 } 212 213 /* Create SSL_CTX. */ 214 if ((ctx = create_ctx(argv[2], argv[3])) == NULL) 215 goto err; 216 217 /* Parse port number from command line arguments. */ 218 port = strtoul(argv[1], NULL, 0); 219 if (port == 0 || port > UINT16_MAX) { 220 fprintf(stderr, "invalid port: %lu\n", port); 221 goto err; 222 } 223 224 /* Create UDP socket. */ 225 if ((fd = create_socket((uint16_t)port)) < 0) 226 goto err; 227 228 /* Enter QUIC server connection acceptance loop. */ 229 if (!run_quic_server(ctx, fd)) 230 goto err; 231 232 rc = 0; 233 err: 234 if (rc != 0) 235 ERR_print_errors_fp(stderr); 236 237 SSL_CTX_free(ctx); 238 239 if (fd != -1) 240 BIO_closesocket(fd); 241 242 return rc; 243 } 244