Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2019 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <logging/log.h>
#define LOG_LEVEL LOG_LEVEL_DBG
LOG_MODULE_REGISTER(net_dumb_http_srv_mt_sample);

#include <zephyr.h>
#include <errno.h>
#include <net/net_ip.h>
#include <net/socket.h>
#include <net/tls_credentials.h>

#define MY_PORT 8080

#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
#define STACK_SIZE 4096

#define SERVER_CERTIFICATE_TAG 1

static const unsigned char server_certificate[] = {
#include "mt-http-server-cert.der.inc"
};

/* This is the private key in pkcs#8 format. */
static const unsigned char private_key[] = {
#include "mt-http-server-key.der.inc"
};
#else
#define STACK_SIZE 1024
#endif
#define THREAD_PRIORITY K_PRIO_COOP(0)

static const char content[] = {
    #include "response_big.html.bin.inc"
};

#define MAX_CLIENT_QUEUE CONFIG_NET_SAMPLE_NUM_HANDLERS

#if defined(CONFIG_NET_IPV4)
K_THREAD_STACK_ARRAY_DEFINE(tcp4_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS,
			    STACK_SIZE);
static struct k_thread tcp4_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS];
static k_tid_t tcp4_handler_tid[CONFIG_NET_SAMPLE_NUM_HANDLERS];
#endif

#if defined(CONFIG_NET_IPV6)
K_THREAD_STACK_ARRAY_DEFINE(tcp6_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS,
			    STACK_SIZE);
static struct k_thread tcp6_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS];
static k_tid_t tcp6_handler_tid[CONFIG_NET_SAMPLE_NUM_HANDLERS];
#endif

static int tcp4_listen_sock;
static int tcp4_accepted[CONFIG_NET_SAMPLE_NUM_HANDLERS];
static int tcp6_listen_sock;
static int tcp6_accepted[CONFIG_NET_SAMPLE_NUM_HANDLERS];

static void process_tcp4(void);
static void process_tcp6(void);

K_THREAD_DEFINE(tcp4_thread_id, STACK_SIZE,
		process_tcp4, NULL, NULL, NULL,
		THREAD_PRIORITY, 0, K_FOREVER);

K_THREAD_DEFINE(tcp6_thread_id, STACK_SIZE,
		process_tcp6, NULL, NULL, NULL,
		THREAD_PRIORITY, 0, K_FOREVER);

static ssize_t sendall(int sock, const void *buf, size_t len)
{
	while (len) {
		ssize_t out_len = send(sock, buf, len, 0);

		if (out_len < 0) {
			return out_len;
		}

		buf = (const char *)buf + out_len;
		len -= out_len;
	}

	return 0;
}

static int setup(int *sock, struct sockaddr *bind_addr,
		 socklen_t bind_addrlen)
{
	int ret;

#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
	*sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TLS_1_2);
#else
	*sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TCP);
#endif
	if (*sock < 0) {
		LOG_ERR("Failed to create TCP socket: %d", errno);
		return -errno;
	}

#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
	sec_tag_t sec_tag_list[] = {
		SERVER_CERTIFICATE_TAG,
	};

	ret = setsockopt(*sock, SOL_TLS, TLS_SEC_TAG_LIST,
			 sec_tag_list, sizeof(sec_tag_list));
	if (ret < 0) {
		LOG_ERR("Failed to set TCP secure option %d", errno);
		ret = -errno;
	}
#endif

	ret = bind(*sock, bind_addr, bind_addrlen);
	if (ret < 0) {
		LOG_ERR("Failed to bind TCP socket %d", errno);
		return -errno;
	}

	ret = listen(*sock, MAX_CLIENT_QUEUE);
	if (ret < 0) {
		LOG_ERR("Failed to listen on TCP socket %d", errno);
		ret = -errno;
	}

	return ret;
}

static void client_conn_handler(void *ptr1, void *ptr2, void *ptr3)
{
	ARG_UNUSED(ptr1);
	int *sock = ptr2;
	k_tid_t *in_use = ptr3;
	int client;
	int received;
	int ret;
	char buf[100];

	client = *sock;

	/* Discard HTTP request (or otherwise client will get
	 * connection reset error).
	 */
	do {
		received = recv(client, buf, sizeof(buf), 0);
		if (received == 0) {
			/* Connection closed */
			LOG_DBG("[%d] Connection closed by peer", client);
			ret = 0;
			break;
		} else if (received < 0) {
			/* Socket error */
			ret = -errno;
			LOG_ERR("[%d] Connection error %d", client, ret);
			break;
		}

		/* Note that something like this strstr() check should *NOT*
		 * be used in production code. This is done like this just
		 * for this sample application to keep things simple.
		 *
		 * We are assuming here that the full HTTP request is received
		 * in one TCP segment which in real life might not.
		 */
		if (strstr(buf, "\r\n\r\n")) {
			break;
		}
	} while (true);

	(void)sendall(client, content, sizeof(content));
	(void)close(client);

	*sock = -1;
	*in_use = NULL;
}

static int get_free_slot(int *accepted)
{
	int i;

	for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) {
		if (accepted[i] < 0) {
			return i;
		}
	}

	return -1;
}

static int process_tcp(int *sock, int *accepted)
{
	static int counter;
	int client;
	int slot;
	struct sockaddr_in6 client_addr;
	socklen_t client_addr_len = sizeof(client_addr);

	client = accept(*sock, (struct sockaddr *)&client_addr,
			&client_addr_len);
	if (client < 0) {
		LOG_ERR("Error in accept %d, stopping server", -errno);
		return -errno;
	}

	slot = get_free_slot(accepted);
	if (slot < 0 || slot >= CONFIG_NET_SAMPLE_NUM_HANDLERS) {
		LOG_ERR("Cannot accept more connections");
		close(client);
		return 0;
	}

	accepted[slot] = client;

#if defined(CONFIG_NET_IPV6)
	if (client_addr.sin6_family == AF_INET6) {
		tcp6_handler_tid[slot] = k_thread_create(
			&tcp6_handler_thread[slot],
			tcp6_handler_stack[slot],
			K_THREAD_STACK_SIZEOF(tcp6_handler_stack[slot]),
			(k_thread_entry_t)client_conn_handler,
			INT_TO_POINTER(slot),
			&accepted[slot],
			&tcp6_handler_tid[slot],
			THREAD_PRIORITY,
			0, K_NO_WAIT);
	}
#endif

#if defined(CONFIG_NET_IPV4)
	if (client_addr.sin6_family == AF_INET) {
		tcp4_handler_tid[slot] = k_thread_create(
			&tcp4_handler_thread[slot],
			tcp4_handler_stack[slot],
			K_THREAD_STACK_SIZEOF(tcp4_handler_stack[slot]),
			(k_thread_entry_t)client_conn_handler,
			INT_TO_POINTER(slot),
			&accepted[slot],
			&tcp4_handler_tid[slot],
			THREAD_PRIORITY,
			0, K_NO_WAIT);
	}
#endif

	if (LOG_LEVEL >= LOG_LEVEL_DBG) {
		char addr_str[INET6_ADDRSTRLEN];

		net_addr_ntop(client_addr.sin6_family,
			      &client_addr.sin6_addr,
			      addr_str, sizeof(addr_str));

		LOG_DBG("[%d] Connection #%d from %s",
			client, ++counter,
			log_strdup(addr_str));
	}

	return 0;
}

static void process_tcp4(void)
{
	struct sockaddr_in addr4;
	int ret;

	(void)memset(&addr4, 0, sizeof(addr4));
	addr4.sin_family = AF_INET;
	addr4.sin_port = htons(MY_PORT);

	ret = setup(&tcp4_listen_sock, (struct sockaddr *)&addr4,
		    sizeof(addr4));
	if (ret < 0) {
		return;
	}

	LOG_DBG("Waiting for IPv4 HTTP connections on port %d, sock %d",
		MY_PORT, tcp4_listen_sock);

	while (ret == 0) {
		ret = process_tcp(&tcp4_listen_sock, tcp4_accepted);
		if (ret < 0) {
			return;
		}
	}
}

static void process_tcp6(void)
{
	struct sockaddr_in6 addr6;
	int ret;

	(void)memset(&addr6, 0, sizeof(addr6));
	addr6.sin6_family = AF_INET6;
	addr6.sin6_port = htons(MY_PORT);

	ret = setup(&tcp6_listen_sock, (struct sockaddr *)&addr6,
		    sizeof(addr6));
	if (ret < 0) {
		return;
	}

	LOG_DBG("Waiting for IPv6 HTTP connections on port %d, sock %d",
		MY_PORT, tcp6_listen_sock);

	while (ret == 0) {
		ret = process_tcp(&tcp6_listen_sock, tcp6_accepted);
		if (ret != 0) {
			return;
		}
	}
}

void start_listener(void)
{
	int i;

	for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) {
#if defined(CONFIG_NET_IPV4)
		tcp4_accepted[i] = -1;
		tcp4_listen_sock = -1;
#endif
#if defined(CONFIG_NET_IPV6)
		tcp6_accepted[i] = -1;
		tcp6_listen_sock = -1;
#endif
	}

	if (IS_ENABLED(CONFIG_NET_IPV6)) {
		k_thread_start(tcp6_thread_id);
	}

	if (IS_ENABLED(CONFIG_NET_IPV4)) {
		k_thread_start(tcp4_thread_id);
	}
}

void main(void)
{
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
	int err = tls_credential_add(SERVER_CERTIFICATE_TAG,
				     TLS_CREDENTIAL_SERVER_CERTIFICATE,
				     server_certificate,
				     sizeof(server_certificate));
	if (err < 0) {
		LOG_ERR("Failed to register public certificate: %d", err);
	}

	err = tls_credential_add(SERVER_CERTIFICATE_TAG,
				 TLS_CREDENTIAL_PRIVATE_KEY,
				 private_key, sizeof(private_key));
	if (err < 0) {
		LOG_ERR("Failed to register private key: %d", err);
	}
#endif

	start_listener();
}