/* * Copyright (c) 2019 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #define LOG_LEVEL LOG_LEVEL_DBG LOG_MODULE_REGISTER(net_dumb_http_srv_mt_sample); #include #include #include #include #include #include #include #include #define MY_PORT 8080 /* If accept returns an error, then we are probably running * out of resource. Sleep a small amount of time in order the * system to cool down. */ #define ACCEPT_ERROR_WAIT 100 /* in ms */ #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 #if defined(CONFIG_NET_TC_THREAD_COOPERATIVE) #define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1) #else #define THREAD_PRIORITY K_PRIO_PREEMPT(8) #endif static const char content[] = { #if defined(CONFIG_NET_SAMPLE_SERVE_LARGE_FILE) #include "response_100k.html.bin.inc" #else #include "response_big.html.bin.inc" #endif }; #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 struct net_mgmt_event_callback mgmt_cb; static bool connected; K_SEM_DEFINE(run_app, 0, 1); K_SEM_DEFINE(quit_lock, 0, 1); static bool running_status; static bool want_to_quit; 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, -1); K_THREAD_DEFINE(tcp6_thread_id, STACK_SIZE, process_tcp6, NULL, NULL, NULL, THREAD_PRIORITY, 0, -1); #define EVENT_MASK (NET_EVENT_L4_CONNECTED | \ NET_EVENT_L4_DISCONNECTED) static void event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface) { if ((mgmt_event & EVENT_MASK) != mgmt_event) { return; } if (want_to_quit) { k_sem_give(&run_app); want_to_quit = false; } if (mgmt_event == NET_EVENT_L4_CONNECTED) { LOG_INF("Network connected"); connected = true; k_sem_give(&run_app); return; } if (mgmt_event == NET_EVENT_L4_DISCONNECTED) { if (connected == false) { LOG_INF("Waiting network to be connected"); } else { LOG_INF("Network disconnected"); connected = false; } k_sem_reset(&run_app); return; } } 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); 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); /* We received status from the client */ if (strstr(buf, "\r\n\r\nOK")) { running_status = true; want_to_quit = true; k_sem_give(&quit_lock); } else if (strstr(buf, "\r\n\r\nFAIL")) { running_status = false; want_to_quit = true; k_sem_give(&quit_lock); } else { (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_DBG("Error in accept %d, ignored", -errno); k_msleep(ACCEPT_ERROR_WAIT); return 0; } 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]), 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]), 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, 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 || !want_to_quit) { 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 || !want_to_quit) { 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); } } int 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 if (IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) { net_mgmt_init_event_callback(&mgmt_cb, event_handler, EVENT_MASK); net_mgmt_add_event_callback(&mgmt_cb); conn_mgr_mon_resend_status(); } if (!IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) { /* If the config library has not been configured to start the * app only after we have a connection, then we can start * it right away. */ k_sem_give(&run_app); } /* Wait for the connection. */ k_sem_take(&run_app, K_FOREVER); start_listener(); k_sem_take(&quit_lock, K_FOREVER); if (running_status) { /* No issues, let the testing system know about this */ exit(0); } else { exit(1); } return 0; }