#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #include /* gai_strerror() */ #include /* write() */ #include /* O_BINARY */ #else #include #include #include #include #endif /* _WIN32 */ #include #include #include #include #include #include /* rustls.h is autogenerated in the Makefile using cbindgen. */ #include "rustls.h" #include "common.h" typedef enum exchange_state { READING_REQUEST, SENT_RESPONSE } exchange_state; /* * Do one read from the socket, and process all resulting bytes into the * rustls_connection, then copy all plaintext bytes from the session to stdout. * Returns: * - DEMO_OK for success * - DEMO_AGAIN if we got an EAGAIN or EWOULDBLOCK reading from the * socket * - DEMO_EOF if we got EOF * - DEMO_ERROR for other errors. */ enum demo_result do_read(struct conndata *conn, struct rustls_connection *rconn) { size_t n = 0; char buf[1]; int err = rustls_connection_read_tls(rconn, read_cb, conn, &n); if(err == EAGAIN || err == EWOULDBLOCK) { LOG("reading from socket: EAGAIN or EWOULDBLOCK: %s", strerror(errno)); return DEMO_AGAIN; } else if(err != 0) { LOG("reading from socket: errno %d", err); return DEMO_ERROR; } if(n == 0) { return DEMO_EOF; } LOG("read %zu bytes from socket", n); unsigned int result = rustls_connection_process_new_packets(rconn); if(result != RUSTLS_RESULT_OK) { print_error("in process_new_packets", result); return DEMO_ERROR; } result = copy_plaintext_to_buffer(conn); if(result != DEMO_EOF) { LOG("do_read returning %d", result); return result; } /* If we got an EOF on the plaintext stream (peer closed connection cleanly), * verify that the sender then closed the TCP connection. */ ssize_t signed_n = read(conn->fd, buf, sizeof(buf)); if(signed_n > 0) { LOG("error: read returned %zu bytes after receiving close_notify", n); return DEMO_ERROR; } else if(signed_n < 0 && errno != EWOULDBLOCK) { LOG("wrong error after receiving close_notify: %s", strerror(errno)); return DEMO_ERROR; } return DEMO_EOF; } enum demo_result send_response(struct conndata *conn) { struct rustls_connection *rconn = conn->rconn; const char *prefix = "HTTP/1.1 200 OK\r\nContent-Length:"; const int body_size = 10000; size_t response_size = strlen(prefix) + 15 + body_size; char *response = malloc(response_size); size_t n; if(response == NULL) { LOG_SIMPLE("failed malloc"); return DEMO_ERROR; } n = snprintf(response, response_size, "%s %d\r\n\r\n", prefix, body_size); memset(response + n, 'a', body_size); *(response + n + body_size) = '\n'; *(response + n + body_size + 1) = '\0'; response_size = strlen(response); rustls_connection_write(rconn, (const uint8_t *)response, response_size, &n); free(response); if(n != response_size) { LOG("failed to write all response bytes. wrote %zu", n); return DEMO_ERROR; } return DEMO_OK; } void handle_conn(struct conndata *conn) { fd_set read_fds; fd_set write_fds; size_t n = 0; struct rustls_connection *rconn = conn->rconn; int sockfd = conn->fd; struct timeval tv; enum exchange_state state = READING_REQUEST; int ciphersuite_id; struct rustls_str ciphersuite_name; LOG("acccepted conn on fd %d", conn->fd); for(;;) { FD_ZERO(&read_fds); if(rustls_connection_wants_read(rconn)) { FD_SET(sockfd, &read_fds); } FD_ZERO(&write_fds); if(rustls_connection_wants_write(rconn)) { FD_SET(sockfd, &write_fds); } if(!rustls_connection_wants_read(rconn) && !rustls_connection_wants_write(rconn)) { LOG_SIMPLE("rustls wants neither read nor write. closing connection"); goto cleanup; } tv.tv_sec = 1; tv.tv_usec = 0; int result = select(sockfd + 1, &read_fds, &write_fds, NULL, &tv); if(result == -1) { perror("server: select"); goto cleanup; } if(result == 0) { LOG_SIMPLE("no fds from select, looping"); continue; } if(FD_ISSET(sockfd, &read_fds)) { /* Read all bytes until we get EAGAIN. Then loop again to wind up in select awaiting the next bit of data. */ for(;;) { result = do_read(conn, rconn); if(result == DEMO_AGAIN) { break; } else if(result != DEMO_OK) { goto cleanup; } } } if(FD_ISSET(sockfd, &write_fds)) { int err = write_tls(rconn, conn, &n); if(err != 0) { LOG("error in write_tls: errno %d", err); goto cleanup; } else if(n == 0) { LOG_SIMPLE("write returned 0 from write_tls"); goto cleanup; } } const uint8_t *negotiated_alpn; size_t negotiated_alpn_len; if(state == READING_REQUEST && body_beginning(&conn->data) != NULL) { state = SENT_RESPONSE; LOG_SIMPLE("writing response"); ciphersuite_id = rustls_connection_get_negotiated_ciphersuite(rconn); ciphersuite_name = rustls_connection_get_negotiated_ciphersuite_name(rconn); LOG("negotiated ciphersuite: %.*s (%#x)", (int)ciphersuite_name.len, ciphersuite_name.data, ciphersuite_id); rustls_connection_get_alpn_protocol( rconn, &negotiated_alpn, &negotiated_alpn_len); if(negotiated_alpn != NULL) { LOG("negotiated ALPN protocol: '%.*s'", (int)negotiated_alpn_len, negotiated_alpn); } else { LOG_SIMPLE("no ALPN protocol was negotiated"); } if(send_response(conn) != DEMO_OK) { goto cleanup; } } } cleanup: LOG("closing socket %d", sockfd); if(sockfd > 0) { close(sockfd); } if(conn->data.data) free(conn->data.data); free(conn); } bool shutting_down = false; void handle_signal(int signo) { if(signo == SIGTERM) { LOG_SIMPLE("received SIGTERM, shutting down"); shutting_down = true; } } int main(int argc, const char **argv) { int ret = 1; int sockfd = 0; const struct rustls_crypto_provider *custom_provider = NULL; struct rustls_server_config_builder *config_builder = NULL; const struct rustls_server_config *server_config = NULL; struct rustls_connection *rconn = NULL; const struct rustls_certified_key *certified_key = NULL; struct rustls_slice_bytes alpn_http11; struct rustls_root_cert_store_builder *client_cert_root_store_builder = NULL; const struct rustls_root_cert_store *client_cert_root_store = NULL; struct rustls_web_pki_client_cert_verifier_builder *client_cert_verifier_builder = NULL; struct rustls_client_cert_verifier *client_cert_verifier = NULL; rustls_result result = RUSTLS_RESULT_OK; /* Set this global variable for logging purposes. */ programname = "server"; alpn_http11.data = (unsigned char *)"http/1.1"; alpn_http11.len = 8; #ifndef _WIN32 struct sigaction siga = { 0 }; siga.sa_handler = handle_signal; if(sigaction(SIGTERM, &siga, NULL) == -1) { perror("server: setting a signal handler"); return 1; } #endif /* _WIN32 */ if(argc <= 2) { fprintf(stderr, "usage: %s cert.pem key.pem\n\n" "Listen on port 8443 with the given cert and key.\n", argv[0]); goto cleanup; } const char *custom_ciphersuite_name = getenv("RUSTLS_CIPHERSUITE"); if(custom_ciphersuite_name != NULL) { custom_provider = default_provider_with_custom_ciphersuite(custom_ciphersuite_name); if(custom_provider == NULL) { goto cleanup; } printf("customized to use ciphersuite: %s\n", custom_ciphersuite_name); result = rustls_server_config_builder_new_custom(custom_provider, default_tls_versions, default_tls_versions_len, &config_builder); if(result != RUSTLS_RESULT_OK) { print_error("creating client config builder", result); goto cleanup; } } else { config_builder = rustls_server_config_builder_new(); } certified_key = load_cert_and_key(argv[1], argv[2]); if(certified_key == NULL) { goto cleanup; } rustls_server_config_builder_set_certified_keys( config_builder, &certified_key, 1); rustls_server_config_builder_set_alpn_protocols( config_builder, &alpn_http11, 1); char *auth_cert = getenv("AUTH_CERT"); char *auth_crl = getenv("AUTH_CRL"); if(auth_cert) { char certbuf[10000]; size_t certbuf_len; unsigned result = read_file(auth_cert, certbuf, sizeof(certbuf), &certbuf_len); if(result != DEMO_OK) { goto cleanup; } client_cert_root_store_builder = rustls_root_cert_store_builder_new(); result = rustls_root_cert_store_builder_add_pem( client_cert_root_store_builder, (uint8_t *)certbuf, certbuf_len, true); if(result != RUSTLS_RESULT_OK) { goto cleanup; } result = rustls_root_cert_store_builder_build( client_cert_root_store_builder, &client_cert_root_store); if(result != RUSTLS_RESULT_OK) { goto cleanup; } client_cert_verifier_builder = rustls_web_pki_client_cert_verifier_builder_new(client_cert_root_store); char crlbuf[10000]; size_t crlbuf_len; if(auth_crl) { result = read_file(auth_crl, crlbuf, sizeof(crlbuf), &crlbuf_len); if(result != DEMO_OK) { goto cleanup; } result = rustls_web_pki_client_cert_verifier_builder_add_crl( client_cert_verifier_builder, (uint8_t *)crlbuf, crlbuf_len); if(result != RUSTLS_RESULT_OK) { goto cleanup; } } result = rustls_web_pki_client_cert_verifier_builder_build( client_cert_verifier_builder, &client_cert_verifier); if(result != RUSTLS_RESULT_OK) { goto cleanup; } rustls_server_config_builder_set_client_verifier(config_builder, client_cert_verifier); } result = rustls_server_config_builder_build(config_builder, &server_config); if(result != RUSTLS_RESULT_OK) { print_error("building server config", result); goto cleanup; } #ifdef _WIN32 WSADATA wsa; WSAStartup(MAKEWORD(1, 1), &wsa); #endif sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd < 0) { LOG("making socket: %s", strerror(errno)); } int enable = 1; if(setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(int)) < 0) { print_error("setsockopt(SO_REUSEADDR) failed", 7001); } struct sockaddr_in my_addr, peer_addr; memset(&my_addr, 0, sizeof(struct sockaddr_in)); /* Clear structure */ my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = INADDR_ANY; my_addr.sin_port = htons(8443); my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_in)) == -1) { perror("server: bind"); goto cleanup; } if(listen(sockfd, 50) == -1) { perror("server: listen"); goto cleanup; } LOG("listening on localhost:8443. AUTH_CERT=%s, AUTH_CRL=%s, VECTORED_IO=%s", auth_cert, auth_crl, getenv("VECTORED_IO")); while(!shutting_down) { socklen_t peer_addr_size; peer_addr_size = sizeof(struct sockaddr_in); int clientfd = accept(sockfd, (struct sockaddr *)&peer_addr, &peer_addr_size); if(shutting_down) { break; } if(clientfd < 0) { perror("server: accept"); goto cleanup; } nonblock(clientfd); unsigned int result = rustls_server_connection_new(server_config, &rconn); if(result != RUSTLS_RESULT_OK) { print_error("making session", result); goto cleanup; } struct conndata *conndata; conndata = calloc(1, sizeof(struct conndata)); conndata->fd = clientfd; conndata->rconn = rconn; rustls_connection_set_userdata(rconn, conndata); rustls_connection_set_log_callback(rconn, log_cb); handle_conn(conndata); rustls_connection_free(rconn); rconn = NULL; } // Success! ret = 0; cleanup: rustls_certified_key_free(certified_key); rustls_root_cert_store_builder_free(client_cert_root_store_builder); rustls_root_cert_store_free(client_cert_root_store); rustls_web_pki_client_cert_verifier_builder_free( client_cert_verifier_builder); rustls_client_cert_verifier_free(client_cert_verifier); rustls_server_config_free(server_config); rustls_connection_free(rconn); rustls_crypto_provider_free(custom_provider); if(sockfd > 0) { close(sockfd); } #ifdef _WIN32 WSACleanup(); #endif return ret; }