#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#include <fcntl.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#endif
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <errno.h>
#include <time.h>
#include "rustls.h"
#include "common.h"
enum crustls_demo_result
read_file(const char *filename, char *buf, size_t buflen, size_t *n)
{
FILE *f = fopen(filename, "r");
if(f == NULL) {
fprintf(stderr, "server: opening %s: %s\n", filename, strerror(errno));
return CRUSTLS_DEMO_ERROR;
}
*n = fread(buf, 1, buflen, f);
if(!feof(f)) {
fprintf(stderr, "server: reading %s: %s\n", filename, strerror(errno));
fclose(f);
return CRUSTLS_DEMO_ERROR;
}
fclose(f);
return CRUSTLS_DEMO_OK;
}
typedef enum exchange_state
{
READING_REQUEST,
SENT_RESPONSE
} exchange_state;
enum crustls_demo_result
do_read(struct conndata *conn, struct rustls_connection *rconn)
{
int err = 1;
int result = 1;
size_t n = 0;
ssize_t signed_n = 0;
char buf[1];
err = rustls_connection_read_tls(rconn, read_cb, conn, &n);
if(err == EAGAIN || err == EWOULDBLOCK) {
fprintf(stderr,
"server: reading from socket: EAGAIN or EWOULDBLOCK: %s\n",
strerror(errno));
return CRUSTLS_DEMO_AGAIN;
}
else if(err != 0) {
fprintf(stderr, "server: reading from socket: errno %d\n", err);
return CRUSTLS_DEMO_ERROR;
}
if(n == 0) {
return CRUSTLS_DEMO_EOF;
}
fprintf(stderr, "server: read %zu bytes from socket\n", n);
result = rustls_connection_process_new_packets(rconn);
if(result != RUSTLS_RESULT_OK) {
print_error("server", "in process_new_packets", result);
return CRUSTLS_DEMO_ERROR;
}
result = copy_plaintext_to_buffer(conn);
if(result != CRUSTLS_DEMO_EOF) {
fprintf(stderr, "server: do_read returning %d\n", result);
return result;
}
signed_n = read(conn->fd, buf, sizeof(buf));
if(signed_n > 0) {
fprintf(stderr,
"server: error: read returned %zu bytes after receiving close_notify\n",
n);
return CRUSTLS_DEMO_ERROR;
}
else if (signed_n < 0 && errno != EWOULDBLOCK) {
fprintf(stderr,
"server: error: read returned incorrect error after receiving close_notify: %s\n",
strerror(errno));
return CRUSTLS_DEMO_ERROR;
}
return CRUSTLS_DEMO_EOF;
}
enum crustls_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) {
fprintf(stderr, "server: failed malloc\n");
return CRUSTLS_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) {
fprintf(stderr, "server: failed to write all response bytes. wrote %zu\n", n);
return CRUSTLS_DEMO_ERROR;
}
return CRUSTLS_DEMO_OK;
}
void
handle_conn(struct conndata *conn)
{
int err = 1;
int result = 1;
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;
fprintf(stderr, "server: accepted conn on fd %d\n", 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)) {
fprintf(stderr, "server: rustls wants neither read nor write. closing connection\n");
goto cleanup;
}
tv.tv_sec = 1;
tv.tv_usec = 0;
result = select(sockfd + 1, &read_fds, &write_fds, NULL, &tv);
if(result == -1) {
perror("select");
goto cleanup;
}
if(result == 0) {
fprintf(stderr, "server: no fds from select, looping\n");
continue;
}
if(FD_ISSET(sockfd, &read_fds)) {
for(;;) {
result = do_read(conn, rconn);
if(result == CRUSTLS_DEMO_AGAIN) {
break;
}
else if(result == CRUSTLS_DEMO_EOF) {
goto cleanup;
}
else if(result != CRUSTLS_DEMO_OK) {
goto cleanup;
}
}
}
if(FD_ISSET(sockfd, &write_fds)) {
err = write_tls(rconn, conn, &n);
if(err != 0) {
fprintf(stderr, "server: error in write_tls: errno %d\n", err);
goto cleanup;
}
else if(n == 0) {
fprintf(stderr, "server: write returned 0 from write_tls\n");
goto cleanup;
}
}
const uint8_t *negotiated_alpn;
size_t negotiated_alpn_len;
if(state == READING_REQUEST && body_beginning(&conn->data) != NULL) {
state = SENT_RESPONSE;
fprintf(stderr, "server: writing response\n");
rustls_connection_get_alpn_protocol(rconn, &negotiated_alpn, &negotiated_alpn_len);
if(negotiated_alpn != NULL) {
fprintf(stderr, "server: negotiated ALPN protocol: '%.*s'\n",
(int)negotiated_alpn_len, negotiated_alpn);
} else {
fprintf(stderr, "server: no ALPN protocol was negotiated\n");
}
if(send_response(conn) != CRUSTLS_DEMO_OK) {
goto cleanup;
};
}
}
fprintf(stderr, "server: handle_conn: loop fell through");
cleanup:
fprintf(stderr, "server: closing socket %d\n", sockfd);
if(sockfd > 0) {
close(sockfd);
}
if(conn->data.data)
free(conn->data.data);
free(conn);
}
const struct rustls_certified_key *
load_cert_and_key(const char *certfile, const char *keyfile)
{
char certbuf[10000];
size_t certbuf_len;
char keybuf[10000];
size_t keybuf_len;
int result = read_file(certfile, certbuf, sizeof(certbuf), &certbuf_len);
if(result != CRUSTLS_DEMO_OK) {
return NULL;
}
result = read_file(keyfile, keybuf, sizeof(keybuf), &keybuf_len);
if(result != CRUSTLS_DEMO_OK) {
return NULL;
}
const struct rustls_certified_key *certified_key;
result = rustls_certified_key_build((uint8_t *)certbuf,
certbuf_len,
(uint8_t *)keybuf,
keybuf_len,
&certified_key);
if(result != RUSTLS_RESULT_OK) {
print_error("server", "parsing certificate and key", result);
return NULL;
}
return certified_key;
}
bool shutting_down = false;
void handle_signal(int signo) {
if(signo == SIGTERM) {
fprintf(stderr, "server: received SIGTERM, shutting down\n");
shutting_down = true;
}
}
int
main(int argc, const char **argv)
{
int ret = 1;
int result = 1;
int sockfd = 0;
struct rustls_server_config_builder *config_builder =
rustls_server_config_builder_new();
const struct rustls_server_config *server_config = NULL;
struct rustls_connection *rconn = NULL;
struct rustls_slice_bytes alpn_http11;
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("setting a signal handler");
return 1;
}
#endif
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 struct rustls_certified_key *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);
server_config = rustls_server_config_builder_build(config_builder);
#ifdef _WIN32
WSADATA wsa;
WSAStartup(MAKEWORD(1, 1), &wsa);
#endif
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) {
fprintf(stderr, "server: making socket: %s", strerror(errno));
}
int enable = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
print_error("server", "setsockopt(SO_REUSEADDR) failed", 7001);
}
struct sockaddr_in my_addr, peer_addr;
memset(&my_addr, 0, sizeof(struct sockaddr_in));
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("bind");
goto cleanup;
}
if(listen(sockfd, 50) == -1) {
perror("listen");
goto cleanup;
}
fprintf(stderr, "server: listening on localhost:8443\n");
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("accept");
goto cleanup;
}
nonblock(clientfd);
result = rustls_server_connection_new(server_config, &rconn);
if(result != RUSTLS_RESULT_OK) {
print_error("server", "making session", result);
goto cleanup;
}
struct conndata *conndata;
conndata = calloc(1, sizeof(struct conndata));
conndata->fd = clientfd;
conndata->rconn = rconn;
conndata->program_name = "server";
rustls_connection_set_userdata(rconn, conndata);
rustls_connection_set_log_callback(rconn, log_cb);
handle_conn(conndata);
rustls_connection_free(rconn);
rconn = NULL;
}
ret = 0;
cleanup:
rustls_certified_key_free(certified_key);
rustls_server_config_free(server_config);
rustls_connection_free(rconn);
if(sockfd>0) {
close(sockfd);
}
#ifdef _WIN32
WSACleanup();
#endif
return ret;
}