1
0

quic-hq-interop-server.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  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. /**
  10. * @file quic-hq-interop-server.c
  11. * @brief Minimal QUIC HTTP/0.9 server implementation.
  12. *
  13. * This file implements a lightweight QUIC server supporting the HTTP/0.9
  14. * protocol for interoperability testing. It includes functions for setting
  15. * up a secure QUIC connection, handling ALPN negotiation, and serving client
  16. * requests. Intended for use with the quic-interop-runner
  17. * available at https://interop.seemann.io
  18. *
  19. * Key functionalities:
  20. * - Setting up SSL_CTX with QUIC support.
  21. * - Negotiating ALPN strings during the TLS handshake.
  22. * - Listening and accepting incoming QUIC connections.
  23. * - Handling client requests via HTTP/0.9 protocol.
  24. *
  25. * Usage:
  26. * <port> <server.crt> <server.key>
  27. * The server binds to the specified port and serves files using the given
  28. * certificate and private key.
  29. *
  30. * Environment variables:
  31. * - FILEPREFIX: Specifies the directory containing files to serve.
  32. * Defaults to "./downloads" if not set.
  33. * - SSLKEYLOGFILE: specifies that keylogging should be preformed on the server
  34. * should be set to a file name to record keylog data to
  35. * - NO_ADDR_VALIDATE: Disables server address validation of clients
  36. *
  37. */
  38. #include <string.h>
  39. /* Include the appropriate header file for SOCK_STREAM */
  40. #ifdef _WIN32
  41. # include <stdarg.h>
  42. # include <winsock2.h>
  43. # include <ws2tcpip.h>
  44. #else
  45. # include <sys/socket.h>
  46. # include <netinet/in.h>
  47. # include <unistd.h>
  48. #endif
  49. #include <openssl/bio.h>
  50. #include <openssl/ssl.h>
  51. #include <openssl/err.h>
  52. #include <openssl/quic.h>
  53. #define BUF_SIZE 4096
  54. /**
  55. * @brief ALPN (Application-Layer Protocol Negotiation) identifier for QUIC.
  56. *
  57. * This constant defines the ALPN string used during the TLS handshake
  58. * to negotiate the application-layer protocol between the client and
  59. * the server. It specifies "hq-interop" as the supported protocol.
  60. *
  61. * Format:
  62. * - The first byte represents the length of the ALPN string.
  63. * - Subsequent bytes represent the ASCII characters of the protocol name.
  64. *
  65. * Value:
  66. * - Protocol: "hq-interop"
  67. * - Length: 10 bytes
  68. *
  69. * Usage:
  70. * This is passed to the ALPN callback function to validate and
  71. * negotiate the desired protocol during the TLS handshake.
  72. */
  73. static const unsigned char alpn_ossltest[] = {
  74. 10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p',
  75. };
  76. /**
  77. * @brief Directory prefix for serving requested files.
  78. *
  79. * This variable specifies the directory path used as the base location
  80. * for serving files in response to client requests. It is used to construct
  81. * the full file path for requested resources.
  82. *
  83. * Default:
  84. * - If not set via the FILEPREFIX environment variable, it defaults to
  85. * "./downloads".
  86. *
  87. * Usage:
  88. * - Updated at runtime based on the FILEPREFIX environment variable.
  89. * - Used to locate and serve files during incoming requests.
  90. */
  91. static char *fileprefix = NULL;
  92. /**
  93. * @brief Callback for ALPN (Application-Layer Protocol Negotiation) selection.
  94. *
  95. * This function is invoked during the TLS handshake on the server side to
  96. * validate and negotiate the desired ALPN (Application-Layer Protocol
  97. * Negotiation) protocol with the client. It ensures that the negotiated
  98. * protocol matches the predefined "hq-interop" string.
  99. *
  100. * @param ssl Pointer to the SSL connection object.
  101. * @param[out] out Pointer to the negotiated ALPN protocol string.
  102. * @param[out] out_len Length of the negotiated ALPN protocol string.
  103. * @param in Pointer to the client-provided ALPN protocol list.
  104. * @param in_len Length of the client-provided ALPN protocol list.
  105. * @param arg Optional user-defined argument (unused in this context).
  106. *
  107. * @return SSL_TLSEXT_ERR_OK on successful ALPN negotiation,
  108. * SSL_TLSEXT_ERR_ALERT_FATAL otherwise.
  109. *
  110. * Usage:
  111. * - This function is set as the ALPN selection callback in the SSL_CTX
  112. * using `SSL_CTX_set_alpn_select_cb`.
  113. * - Ensures that only the predefined ALPN protocol is accepted.
  114. *
  115. * Note:
  116. * - The predefined protocol is specified in the `alpn_ossltest` array.
  117. */
  118. static int select_alpn(SSL *ssl, const unsigned char **out,
  119. unsigned char *out_len, const unsigned char *in,
  120. unsigned int in_len, void *arg)
  121. {
  122. /*
  123. * Use the next_proto helper function here.
  124. * This scans the list of alpns we support and matches against
  125. * what the client is requesting
  126. */
  127. if (SSL_select_next_proto((unsigned char **)out, out_len, alpn_ossltest,
  128. sizeof(alpn_ossltest), in,
  129. in_len) == OPENSSL_NPN_NEGOTIATED)
  130. return SSL_TLSEXT_ERR_OK;
  131. return SSL_TLSEXT_ERR_ALERT_FATAL;
  132. }
  133. /**
  134. * @brief Creates and configures an SSL_CTX for a QUIC server.
  135. *
  136. * This function initializes an SSL_CTX object with the QUIC server method
  137. * and configures it using the provided certificate and private key. The
  138. * context is prepared for handling secure QUIC connections and performing
  139. * ALPN (Application-Layer Protocol Negotiation).
  140. *
  141. * @param cert_path Path to the server's certificate chain file in PEM format.
  142. * The chain file must include the server's leaf certificate
  143. * followed by intermediate CA certificates.
  144. * @param key_path Path to the server's private key file in PEM format. The
  145. * private key must correspond to the leaf certificate in
  146. * the chain file.
  147. *
  148. * @return Pointer to the initialized SSL_CTX on success, or NULL on failure.
  149. *
  150. * Configuration:
  151. * - Loads the certificate chain and private key into the context.
  152. * - Disables client certificate verification (no mutual TLS).
  153. * - Sets up the ALPN selection callback for protocol negotiation.
  154. *
  155. * Error Handling:
  156. * - If any step fails (e.g., loading the certificate or key), the function
  157. * frees the SSL_CTX and returns NULL.
  158. *
  159. * Usage:
  160. * - Call this function to create an SSL_CTX before starting the QUIC server.
  161. * - Ensure valid paths for the certificate and private key are provided.
  162. *
  163. * Note:
  164. * - The ALPN callback only supports the predefined protocol defined in
  165. * `alpn_ossltest`.
  166. */
  167. static SSL_CTX *create_ctx(const char *cert_path, const char *key_path)
  168. {
  169. SSL_CTX *ctx;
  170. /*
  171. * An SSL_CTX holds shared configuration information for multiple
  172. * subsequent per-client connections. We specifically load a QUIC
  173. * server method here.
  174. */
  175. ctx = SSL_CTX_new(OSSL_QUIC_server_method());
  176. if (ctx == NULL)
  177. goto err;
  178. /*
  179. * Load the server's certificate *chain* file (PEM format), which includes
  180. * not only the leaf (end-entity) server certificate, but also any
  181. * intermediate issuer-CA certificates. The leaf certificate must be the
  182. * first certificate in the file.
  183. *
  184. * In advanced use-cases this can be called multiple times, once per public
  185. * key algorithm for which the server has a corresponding certificate.
  186. * However, the corresponding private key (see below) must be loaded first,
  187. * *before* moving on to the next chain file.
  188. *
  189. * The requisite files "chain.pem" and "pkey.pem" can be generated by running
  190. * "make chain" in this directory. If the server will be executed from some
  191. * other directory, move or copy the files there.
  192. */
  193. if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) <= 0) {
  194. fprintf(stderr, "couldn't load certificate file: %s\n", cert_path);
  195. goto err;
  196. }
  197. /*
  198. * Load the corresponding private key, this also checks that the private
  199. * key matches the just loaded end-entity certificate. It does not check
  200. * whether the certificate chain is valid, the certificates could be
  201. * expired, or may otherwise fail to form a chain that a client can validate.
  202. */
  203. if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
  204. fprintf(stderr, "couldn't load key file: %s\n", key_path);
  205. goto err;
  206. }
  207. /*
  208. * Since we're not soliciting or processing client certificates, we don't
  209. * need to configure a trusted-certificate store, so no call to
  210. * SSL_CTX_set_default_verify_paths() is needed. The server's own
  211. * certificate chain is assumed valid.
  212. */
  213. SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
  214. /* Setup ALPN negotiation callback to decide which ALPN is accepted. */
  215. SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL);
  216. return ctx;
  217. err:
  218. SSL_CTX_free(ctx);
  219. return NULL;
  220. }
  221. /**
  222. * @brief Creates and binds a UDP socket to the specified port.
  223. *
  224. * This function initializes a new UDP socket, binds it to the specified
  225. * port on the local host, and returns the socket file descriptor for
  226. * further use.
  227. *
  228. * @param port The port number to which the UDP socket should be bound.
  229. *
  230. * @return On success, returns the BIO of the created socket.
  231. * On failure, returns NULL.
  232. *
  233. * Steps:
  234. * - Creates a new UDP socket using the `socket` system call.
  235. * - Configures the socket address structure to bind to the specified port
  236. * on the local host.
  237. * - Binds the socket to the port using the `bind` system call.
  238. *
  239. * Error Handling:
  240. * - If socket creation or binding fails, an error message is printed to
  241. * `stderr`, the socket (if created) is closed, and -1 is returned.
  242. *
  243. * Usage:
  244. * - Call this function to set up a socket for handling incoming QUIC
  245. * connections.
  246. *
  247. * Notes:
  248. * - This function assumes UDP (`SOCK_DGRAM`).
  249. * - This function accepts on both IPv4 and IPv6.
  250. * - The specified port is converted to network byte order using `htons`.
  251. */
  252. static BIO *create_socket(uint16_t port)
  253. {
  254. int fd = -1;
  255. BIO *sock = NULL;
  256. BIO_ADDR *addr = NULL;
  257. int opt = 0;
  258. #ifdef _WIN32
  259. struct in6_addr in6addr_any;
  260. memset(&in6addr_any, 0, sizeof(in6addr_any));
  261. #endif
  262. /* Retrieve the file descriptor for a new UDP socket */
  263. if ((fd = BIO_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0)) < 0) {
  264. fprintf(stderr, "cannot create socket");
  265. goto err;
  266. }
  267. /*
  268. * IPv6_V6ONLY is only available on some platforms. If it is defined,
  269. * disable it to accept both IPv4 and IPv6 connections. Otherwise, the
  270. * server will only accept IPv6 connections.
  271. */
  272. #ifdef IPV6_V6ONLY
  273. if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) {
  274. fprintf(stderr, "setsockopt IPV6_V6ONLY failed");
  275. goto err;
  276. }
  277. #endif
  278. /*
  279. * Create a new BIO_ADDR
  280. */
  281. addr = BIO_ADDR_new();
  282. if (addr == NULL) {
  283. fprintf(stderr, "Unable to create BIO_ADDR\n");
  284. goto err;
  285. }
  286. /*
  287. * Build an INADDR_ANY BIO_ADDR
  288. */
  289. if (!BIO_ADDR_rawmake(addr, AF_INET6, &in6addr_any, sizeof(in6addr_any), htons(port))) {
  290. fprintf(stderr, "unable to bind to port %d\n", port);
  291. goto err;
  292. }
  293. /* Bind to the new UDP socket */
  294. if (!BIO_bind(fd, addr, 0)) {
  295. fprintf(stderr, "cannot bind to %u\n", port);
  296. goto err;
  297. }
  298. /*
  299. * Create a new datagram socket
  300. */
  301. sock = BIO_new(BIO_s_datagram());
  302. if (sock == NULL) {
  303. fprintf(stderr, "cannot create dgram bio\n");
  304. goto err;
  305. }
  306. /*
  307. * associate the underlying socket with the dgram BIO
  308. */
  309. if (!BIO_set_fd(sock, fd, BIO_CLOSE)) {
  310. fprintf(stderr, "Unable to set fd of dgram sock\n");
  311. goto err;
  312. }
  313. /*
  314. * Free our allocated addr
  315. */
  316. BIO_ADDR_free(addr);
  317. return sock;
  318. err:
  319. BIO_ADDR_free(addr);
  320. BIO_free(sock);
  321. BIO_closesocket(fd);
  322. return NULL;
  323. }
  324. /**
  325. * @brief Handles I/O failures on an SSL stream based on the result code.
  326. *
  327. * This function processes the result of an SSL I/O operation and handles
  328. * different types of errors that may occur during the operation. It takes
  329. * appropriate actions such as retrying the operation, reporting errors, or
  330. * returning specific status codes based on the error type.
  331. *
  332. * @param ssl A pointer to the SSL object representing the stream.
  333. * @param res The result code from the SSL I/O operation.
  334. * @return An integer indicating the outcome:
  335. * - 0: EOF, indicating the stream has been closed.
  336. * - -1: A fatal error occurred or the stream has been reset.
  337. *
  338. *
  339. * @note If the failure is due to an SSL verification error, additional
  340. * information will be logged to stderr.
  341. */
  342. static int handle_io_failure(SSL *ssl, int res)
  343. {
  344. switch (SSL_get_error(ssl, res)) {
  345. case SSL_ERROR_ZERO_RETURN:
  346. /* EOF */
  347. return 0;
  348. case SSL_ERROR_SYSCALL:
  349. return -1;
  350. case SSL_ERROR_SSL:
  351. /*
  352. * Some stream fatal error occurred. This could be because of a
  353. * stream reset - or some failure occurred on the underlying
  354. * connection.
  355. */
  356. switch (SSL_get_stream_read_state(ssl)) {
  357. case SSL_STREAM_STATE_RESET_REMOTE:
  358. fprintf(stderr, "Stream reset occurred\n");
  359. /*
  360. * The stream has been reset but the connection is still
  361. * healthy.
  362. */
  363. break;
  364. case SSL_STREAM_STATE_CONN_CLOSED:
  365. fprintf(stderr, "Connection closed\n");
  366. /* Connection is already closed. */
  367. break;
  368. default:
  369. fprintf(stderr, "Unknown stream failure\n");
  370. break;
  371. }
  372. /*
  373. * If the failure is due to a verification error we can get more
  374. * information about it from SSL_get_verify_result().
  375. */
  376. if (SSL_get_verify_result(ssl) != X509_V_OK)
  377. fprintf(stderr, "Verify error: %s\n",
  378. X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
  379. return -1;
  380. default:
  381. return -1;
  382. }
  383. }
  384. /**
  385. * @brief Processes a new incoming QUIC stream for an HTTP/0.9 GET request.
  386. *
  387. * This function reads an HTTP/0.9 GET request from the provided QUIC stream,
  388. * retrieves the requested file from the server's file system, and sends the
  389. * file contents back to the client over the stream.
  390. *
  391. * @param Pointer to the SSL object representing the QUIC stream.
  392. *
  393. * Operation:
  394. * - Reads the HTTP/0.9 GET request from the client.
  395. * - Parses the request to extract the requested file name.
  396. * - Constructs the file path using the `fileprefix` directory.
  397. * - Reads the requested file in chunks and sends it to the client.
  398. * - Concludes the QUIC stream once the file is fully sent.
  399. *
  400. * Error Handling:
  401. * - If the request is invalid or the file cannot be opened, appropriate
  402. * error messages are logged, and the function exits without sending data.
  403. * - Errors during file reading or writing to the stream are handled, with
  404. * retries for buffer-related issues (e.g., full send buffer).
  405. *
  406. * Notes:
  407. * - The request is expected to be a valid HTTP/0.9 GET request.
  408. * - File paths are sanitized to prevent path traversal vulnerabilities.
  409. * - The function uses blocking operations for reading and writing data.
  410. *
  411. * Usage:
  412. * - Called for each accepted QUIC stream to handle client requests.
  413. */
  414. static void process_new_stream(SSL *stream)
  415. {
  416. unsigned char buf[BUF_SIZE];
  417. char path[BUF_SIZE];
  418. char *req = (char *)buf;
  419. char *reqname;
  420. char *creturn;
  421. size_t nread;
  422. BIO *readbio;
  423. size_t bytes_read = 0;
  424. size_t bytes_written = 0;
  425. size_t offset = 0;
  426. int rc;
  427. int ret;
  428. size_t total_read = 0;
  429. memset(buf, 0, BUF_SIZE);
  430. for (;;) {
  431. nread = 0;
  432. ret = SSL_read_ex(stream, &buf[total_read],
  433. sizeof(buf) - total_read - 1, &nread);
  434. total_read += nread;
  435. if (ret <= 0) {
  436. ret = handle_io_failure(stream, ret);
  437. if (ret == 0) {
  438. /* EOF condition, fin bit set, we got the whole request */
  439. break;
  440. } else {
  441. /* permanent failure, abort */
  442. fprintf(stderr, "Failure on stream\n");
  443. return;
  444. }
  445. }
  446. }
  447. /* We should have a valid http 0.9 GET request here */
  448. fprintf(stderr, "Request is %s\n", req);
  449. /* Look for the last '/' char in the request */
  450. reqname = strrchr(req, '/');
  451. if (reqname == NULL)
  452. return;
  453. reqname++;
  454. /* Requests have a trailing \r\n, eliminate them */
  455. creturn = strchr(reqname, '\r');
  456. if (creturn != NULL)
  457. *creturn = '\0';
  458. snprintf(path, BUF_SIZE, "%s/%s", fileprefix, reqname);
  459. fprintf(stderr, "Serving %s\n", path);
  460. readbio = BIO_new_file(path, "r");
  461. if (readbio == NULL) {
  462. fprintf(stderr, "Unable to open %s\n", path);
  463. ERR_print_errors_fp(stderr);
  464. goto done;
  465. }
  466. /* Read the readbio file into a buffer, and just send it to the requestor */
  467. while (BIO_eof(readbio) <= 0) {
  468. bytes_read = 0;
  469. if (!BIO_read_ex(readbio, buf, BUF_SIZE, &bytes_read)) {
  470. if (BIO_eof(readbio) <= 0) {
  471. fprintf(stderr, "Failed to read from %s\n", path);
  472. ERR_print_errors_fp(stderr);
  473. goto out;
  474. } else {
  475. break;
  476. }
  477. }
  478. offset = 0;
  479. for (;;) {
  480. bytes_written = 0;
  481. rc = SSL_write_ex(stream, &buf[offset], bytes_read, &bytes_written);
  482. if (rc <= 0) {
  483. rc = SSL_get_error(stream, rc);
  484. switch (rc) {
  485. case SSL_ERROR_WANT_WRITE:
  486. fprintf(stderr, "Send buffer full, retrying\n");
  487. continue;
  488. break;
  489. default:
  490. fprintf(stderr, "Unhandled error cause %d\n", rc);
  491. goto done;
  492. break;
  493. }
  494. }
  495. bytes_read -= bytes_written;
  496. offset += bytes_written;
  497. bytes_written = 0;
  498. if (bytes_read == 0)
  499. break;
  500. }
  501. }
  502. done:
  503. if (!SSL_stream_conclude(stream, 0))
  504. fprintf(stderr, "Failed to conclude stream\n");
  505. out:
  506. BIO_free(readbio);
  507. return;
  508. }
  509. /**
  510. * @brief Runs the QUIC server to accept and handle client connections.
  511. *
  512. * This function initializes a QUIC listener, binds it to the provided UDP
  513. * socket, and enters a loop to accept client connections and process incoming
  514. * QUIC streams. Each connection is handled until termination, and streams are
  515. * processed individually using the `process_new_stream` function.
  516. *
  517. * @param ctx Pointer to the SSL_CTX object configured for QUIC.
  518. * @param sock BIO of the bound UDP socket.
  519. *
  520. * @return Returns 0 on error; otherwise, the server runs indefinitely.
  521. *
  522. * Operation:
  523. * - Creates a QUIC listener using the provided SSL_CTX and associates it
  524. * with the specified UDP socket.
  525. * - Waits for incoming QUIC connections and accepts them.
  526. * - For each connection:
  527. * - Accepts incoming streams.
  528. * - Processes each stream using `process_new_stream`.
  529. * - Shuts down the connection upon completion.
  530. *
  531. * Error Handling:
  532. * - If listener creation or connection acceptance fails, the function logs
  533. * an error message and exits the loop.
  534. * - Cleans up allocated resources (e.g., listener, connection) on failure.
  535. *
  536. * Usage:
  537. * - Call this function in the main server loop after setting up the
  538. * SSL_CTX and binding a UDP socket.
  539. *
  540. * Notes:
  541. * - Uses blocking operations for listener, connection, and stream handling.
  542. * - Incoming streams are processed based on the configured stream policy.
  543. * - The server runs in an infinite loop unless a fatal error occurs.
  544. */
  545. static int run_quic_server(SSL_CTX *ctx, BIO *sock)
  546. {
  547. int ok = 0;
  548. SSL *listener, *conn, *stream;
  549. unsigned long errcode;
  550. uint64_t flags = 0;
  551. /*
  552. * If NO_ADDR_VALIDATE exists in our environment
  553. * then disable address validation on our listener
  554. */
  555. if (getenv("NO_ADDR_VALIDATE") != NULL)
  556. flags |= SSL_LISTENER_FLAG_NO_VALIDATE;
  557. /*
  558. * Create a new QUIC listener. Listeners, and other QUIC objects, default
  559. * to operating in blocking mode. The configured behaviour is inherited by
  560. * child objects.
  561. */
  562. if ((listener = SSL_new_listener(ctx, flags)) == NULL)
  563. goto err;
  564. /* Provide the listener with our UDP socket. */
  565. SSL_set_bio(listener, sock, sock);
  566. /* Begin listening. */
  567. if (!SSL_listen(listener))
  568. goto err;
  569. /*
  570. * Begin an infinite loop of listening for connections. We will only
  571. * exit this loop if we encounter an error.
  572. */
  573. for (;;) {
  574. /* Pristine error stack for each new connection */
  575. ERR_clear_error();
  576. /* Block while waiting for a client connection */
  577. printf("Waiting for connection\n");
  578. conn = SSL_accept_connection(listener, 0);
  579. if (conn == NULL) {
  580. fprintf(stderr, "error while accepting connection\n");
  581. goto err;
  582. }
  583. printf("Accepted new connection\n");
  584. /*
  585. * QUIC requires that we inform the connection that
  586. * we always want to accept new streams, rather than reject them
  587. * Additionally, while we don't make an explicit call here, we
  588. * are using the default stream mode, as would be specified by
  589. * a call to SSL_set_default_stream_mode
  590. */
  591. if (!SSL_set_incoming_stream_policy(conn,
  592. SSL_INCOMING_STREAM_POLICY_ACCEPT,
  593. 0)) {
  594. fprintf(stderr, "Failed to set incomming stream policy\n");
  595. goto close_conn;
  596. }
  597. /*
  598. * Until the connection is closed, accept incomming stream
  599. * requests and serve them
  600. */
  601. for (;;) {
  602. /*
  603. * Note that SSL_accept_stream is blocking here, as the
  604. * conn SSL object inherited the deafult blocking property
  605. * from its parent, the listener SSL object. As such there
  606. * is no need to handle retry failures here.
  607. */
  608. stream = SSL_accept_stream(conn, 0);
  609. if (stream == NULL) {
  610. /*
  611. * If we don't get a stream, either we
  612. * Hit a legitimate error, and should bail out
  613. * or
  614. * The Client closed the connection, and there are no
  615. * more incomming streams expected
  616. *
  617. * Filter on the shutdown error, and only print an error
  618. * message if the cause is not SHUTDOWN
  619. */
  620. ERR_print_errors_fp(stderr);
  621. errcode = ERR_get_error();
  622. if (ERR_GET_REASON(errcode) != SSL_R_PROTOCOL_IS_SHUTDOWN)
  623. fprintf(stderr, "Failure in accept stream, error %s\n",
  624. ERR_reason_error_string(errcode));
  625. break;
  626. }
  627. process_new_stream(stream);
  628. SSL_free(stream);
  629. }
  630. /*
  631. * Shut down the connection. We may need to call this multiple times
  632. * to ensure the connection is shutdown completely.
  633. */
  634. close_conn:
  635. while (SSL_shutdown(conn) != 1)
  636. continue;
  637. SSL_free(conn);
  638. }
  639. err:
  640. SSL_free(listener);
  641. return ok;
  642. }
  643. /**
  644. * @brief Entry point for the minimal QUIC HTTP/0.9 server.
  645. *
  646. * This function initializes the server, sets up a QUIC context, binds a UDP
  647. * socket to the specified port, and starts the main QUIC server loop to handle
  648. * client connections and requests.
  649. *
  650. * @param argc Number of command-line arguments.
  651. * @param argv Array of command-line arguments:
  652. * - argv[0]: Program name.
  653. * - argv[1]: Port number to bind the server.
  654. * - argv[2]: Path to the server's certificate file (PEM format).
  655. * - argv[3]: Path to the server's private key file (PEM format).
  656. *
  657. * @return Returns EXIT_SUCCESS on successful execution, or EXIT_FAILURE
  658. * on error.
  659. *
  660. * Operation:
  661. * - Validates the command-line arguments.
  662. * - Reads the FILEPREFIX environment variable to set the file prefix for
  663. * serving files (default is "./downloads").
  664. * - Creates an SSL_CTX with QUIC support using the provided certificate and
  665. * key files.
  666. * - Parses and validates the port number.
  667. * - Creates and binds a UDP socket to the specified port.
  668. * - Starts the server loop using `run_quic_server` to accept and process
  669. * client connections.
  670. *
  671. * Error Handling:
  672. * - If any initialization step fails (e.g., invalid arguments, socket
  673. * creation, context setup), appropriate error messages are logged, and
  674. * the program exits with EXIT_FAILURE.
  675. *
  676. * Usage:
  677. * - Run the program with the required arguments to start the server:
  678. * `./server <port> <server.crt> <server.key>`
  679. *
  680. * Notes:
  681. * - Ensure that the certificate and key files exist and are valid.
  682. * - The server serves files from the directory specified by FILEPREFIX.
  683. */
  684. int main(int argc, char *argv[])
  685. {
  686. int res = EXIT_FAILURE;
  687. SSL_CTX *ctx = NULL;
  688. BIO *sock = NULL;
  689. unsigned long port;
  690. if (argc != 4) {
  691. fprintf(stderr, "usage: %s <port> <server.crt> <server.key>\n", argv[0]);
  692. goto out;
  693. }
  694. fileprefix = getenv("FILEPREFIX");
  695. if (fileprefix == NULL)
  696. fileprefix = "./downloads";
  697. fprintf(stderr, "Fileprefix is %s\n", fileprefix);
  698. /* Create SSL_CTX that supports QUIC. */
  699. if ((ctx = create_ctx(argv[2], argv[3])) == NULL) {
  700. ERR_print_errors_fp(stderr);
  701. fprintf(stderr, "Failed to create context\n");
  702. goto out;
  703. }
  704. /* Parse port number from command line arguments. */
  705. port = strtoul(argv[1], NULL, 0);
  706. if (port == 0 || port > UINT16_MAX) {
  707. fprintf(stderr, "Failed to parse port number\n");
  708. goto out;
  709. }
  710. fprintf(stderr, "Binding to port %lu\n", port);
  711. /* Create and bind a UDP socket. */
  712. if ((sock = create_socket((uint16_t)port)) == NULL) {
  713. ERR_print_errors_fp(stderr);
  714. fprintf(stderr, "Failed to create socket\n");
  715. goto out;
  716. }
  717. /* QUIC server connection acceptance loop. */
  718. if (!run_quic_server(ctx, sock)) {
  719. ERR_print_errors_fp(stderr);
  720. fprintf(stderr, "Failed to run quic server\n");
  721. goto out;
  722. }
  723. res = EXIT_SUCCESS;
  724. out:
  725. /* Free resources. */
  726. SSL_CTX_free(ctx);
  727. BIO_free(sock);
  728. return res;
  729. }