1 /*
2  * Copyright (c) 2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 #define LOG_LEVEL LOG_LEVEL_DBG
9 LOG_MODULE_REGISTER(net_dumb_http_srv_mt_sample);
10 
11 #include <zephyr/posix/sys/socket.h>
12 #include <zephyr/posix/unistd.h>
13 
14 #include <zephyr/kernel.h>
15 #include <errno.h>
16 #include <zephyr/net/net_ip.h>
17 #include <zephyr/net/socket.h>
18 #include <zephyr/net/tls_credentials.h>
19 
20 #include <zephyr/net/net_mgmt.h>
21 #include <zephyr/net/net_event.h>
22 #include <zephyr/net/conn_mgr_monitor.h>
23 
24 #define MY_PORT 8080
25 
26 /* If accept returns an error, then we are probably running
27  * out of resource. Sleep a small amount of time in order the
28  * system to cool down.
29  */
30 #define ACCEPT_ERROR_WAIT 100 /* in ms */
31 
32 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
33 #define STACK_SIZE 4096
34 
35 #define SERVER_CERTIFICATE_TAG 1
36 
37 static const unsigned char server_certificate[] = {
38 #include "mt-http-server-cert.der.inc"
39 };
40 
41 /* This is the private key in pkcs#8 format. */
42 static const unsigned char private_key[] = {
43 #include "mt-http-server-key.der.inc"
44 };
45 #else
46 #define STACK_SIZE 1024
47 #endif
48 #if defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
49 #define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)
50 #else
51 #define THREAD_PRIORITY K_PRIO_PREEMPT(8)
52 #endif
53 
54 static const char content[] = {
55 #if defined(CONFIG_NET_SAMPLE_SERVE_LARGE_FILE)
56     #include "response_100k.html.bin.inc"
57 #else
58     #include "response_big.html.bin.inc"
59 #endif
60 };
61 
62 #define MAX_CLIENT_QUEUE CONFIG_NET_SAMPLE_NUM_HANDLERS
63 
64 #if defined(CONFIG_NET_IPV4)
65 K_THREAD_STACK_ARRAY_DEFINE(tcp4_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS,
66 			    STACK_SIZE);
67 static struct k_thread tcp4_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS];
68 static k_tid_t tcp4_handler_tid[CONFIG_NET_SAMPLE_NUM_HANDLERS];
69 #endif
70 
71 #if defined(CONFIG_NET_IPV6)
72 K_THREAD_STACK_ARRAY_DEFINE(tcp6_handler_stack, CONFIG_NET_SAMPLE_NUM_HANDLERS,
73 			    STACK_SIZE);
74 static struct k_thread tcp6_handler_thread[CONFIG_NET_SAMPLE_NUM_HANDLERS];
75 static k_tid_t tcp6_handler_tid[CONFIG_NET_SAMPLE_NUM_HANDLERS];
76 #endif
77 
78 static struct net_mgmt_event_callback mgmt_cb;
79 static bool connected;
80 K_SEM_DEFINE(run_app, 0, 1);
81 K_SEM_DEFINE(quit_lock, 0, 1);
82 static bool running_status;
83 static bool want_to_quit;
84 static int tcp4_listen_sock;
85 static int tcp4_accepted[CONFIG_NET_SAMPLE_NUM_HANDLERS];
86 static int tcp6_listen_sock;
87 static int tcp6_accepted[CONFIG_NET_SAMPLE_NUM_HANDLERS];
88 
89 static void process_tcp4(void);
90 static void process_tcp6(void);
91 
92 K_THREAD_DEFINE(tcp4_thread_id, STACK_SIZE,
93 		process_tcp4, NULL, NULL, NULL,
94 		THREAD_PRIORITY, 0, -1);
95 
96 K_THREAD_DEFINE(tcp6_thread_id, STACK_SIZE,
97 		process_tcp6, NULL, NULL, NULL,
98 		THREAD_PRIORITY, 0, -1);
99 
100 #define EVENT_MASK (NET_EVENT_L4_CONNECTED | \
101 		    NET_EVENT_L4_DISCONNECTED)
102 
event_handler(struct net_mgmt_event_callback * cb,uint64_t mgmt_event,struct net_if * iface)103 static void event_handler(struct net_mgmt_event_callback *cb,
104 			  uint64_t mgmt_event, struct net_if *iface)
105 {
106 	if ((mgmt_event & EVENT_MASK) != mgmt_event) {
107 		return;
108 	}
109 
110 	if (want_to_quit) {
111 		k_sem_give(&run_app);
112 		want_to_quit = false;
113 	}
114 
115 	if (mgmt_event == NET_EVENT_L4_CONNECTED) {
116 		LOG_INF("Network connected");
117 
118 		connected = true;
119 		k_sem_give(&run_app);
120 
121 		return;
122 	}
123 
124 	if (mgmt_event == NET_EVENT_L4_DISCONNECTED) {
125 		if (connected == false) {
126 			LOG_INF("Waiting network to be connected");
127 		} else {
128 			LOG_INF("Network disconnected");
129 			connected = false;
130 		}
131 
132 		k_sem_reset(&run_app);
133 
134 		return;
135 	}
136 }
137 
sendall(int sock,const void * buf,size_t len)138 static ssize_t sendall(int sock, const void *buf, size_t len)
139 {
140 	while (len) {
141 		ssize_t out_len = send(sock, buf, len, 0);
142 
143 		if (out_len < 0) {
144 			return out_len;
145 		}
146 
147 		buf = (const char *)buf + out_len;
148 		len -= out_len;
149 	}
150 
151 	return 0;
152 }
153 
setup(int * sock,struct sockaddr * bind_addr,socklen_t bind_addrlen)154 static int setup(int *sock, struct sockaddr *bind_addr,
155 		 socklen_t bind_addrlen)
156 {
157 	int ret;
158 
159 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
160 	*sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TLS_1_2);
161 #else
162 	*sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TCP);
163 #endif
164 	if (*sock < 0) {
165 		LOG_ERR("Failed to create TCP socket: %d", errno);
166 		return -errno;
167 	}
168 
169 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
170 	sec_tag_t sec_tag_list[] = {
171 		SERVER_CERTIFICATE_TAG,
172 	};
173 
174 	ret = setsockopt(*sock, SOL_TLS, TLS_SEC_TAG_LIST,
175 			 sec_tag_list, sizeof(sec_tag_list));
176 	if (ret < 0) {
177 		LOG_ERR("Failed to set TCP secure option %d", errno);
178 		ret = -errno;
179 	}
180 #endif
181 
182 	ret = bind(*sock, bind_addr, bind_addrlen);
183 	if (ret < 0) {
184 		LOG_ERR("Failed to bind TCP socket %d", errno);
185 		return -errno;
186 	}
187 
188 	ret = listen(*sock, MAX_CLIENT_QUEUE);
189 	if (ret < 0) {
190 		LOG_ERR("Failed to listen on TCP socket %d", errno);
191 		ret = -errno;
192 	}
193 
194 	return ret;
195 }
196 
client_conn_handler(void * ptr1,void * ptr2,void * ptr3)197 static void client_conn_handler(void *ptr1, void *ptr2, void *ptr3)
198 {
199 	ARG_UNUSED(ptr1);
200 	int *sock = ptr2;
201 	k_tid_t *in_use = ptr3;
202 	int client;
203 	int received;
204 	int ret;
205 	char buf[100];
206 
207 	client = *sock;
208 
209 	/* Discard HTTP request (or otherwise client will get
210 	 * connection reset error).
211 	 */
212 	do {
213 		received = recv(client, buf, sizeof(buf), 0);
214 		if (received == 0) {
215 			/* Connection closed */
216 			LOG_DBG("[%d] Connection closed by peer", client);
217 			break;
218 		} else if (received < 0) {
219 			/* Socket error */
220 			ret = -errno;
221 			LOG_ERR("[%d] Connection error %d", client, ret);
222 			break;
223 		}
224 
225 		/* Note that something like this strstr() check should *NOT*
226 		 * be used in production code. This is done like this just
227 		 * for this sample application to keep things simple.
228 		 *
229 		 * We are assuming here that the full HTTP request is received
230 		 * in one TCP segment which in real life might not.
231 		 */
232 		if (strstr(buf, "\r\n\r\n")) {
233 			break;
234 		}
235 	} while (true);
236 
237 	/* We received status from the client */
238 	if (strstr(buf, "\r\n\r\nOK")) {
239 		running_status = true;
240 		want_to_quit = true;
241 		k_sem_give(&quit_lock);
242 	} else if (strstr(buf, "\r\n\r\nFAIL")) {
243 		running_status = false;
244 		want_to_quit = true;
245 		k_sem_give(&quit_lock);
246 	} else {
247 		(void)sendall(client, content, sizeof(content));
248 	}
249 
250 	(void)close(client);
251 
252 	*sock = -1;
253 	*in_use = NULL;
254 }
255 
get_free_slot(int * accepted)256 static int get_free_slot(int *accepted)
257 {
258 	int i;
259 
260 	for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) {
261 		if (accepted[i] < 0) {
262 			return i;
263 		}
264 	}
265 
266 	return -1;
267 }
268 
process_tcp(int * sock,int * accepted)269 static int process_tcp(int *sock, int *accepted)
270 {
271 	static int counter;
272 	int client;
273 	int slot;
274 	struct sockaddr_in6 client_addr;
275 	socklen_t client_addr_len = sizeof(client_addr);
276 
277 	client = accept(*sock, (struct sockaddr *)&client_addr,
278 			&client_addr_len);
279 	if (client < 0) {
280 		LOG_DBG("Error in accept %d, ignored", -errno);
281 		k_msleep(ACCEPT_ERROR_WAIT);
282 		return 0;
283 	}
284 
285 	slot = get_free_slot(accepted);
286 	if (slot < 0 || slot >= CONFIG_NET_SAMPLE_NUM_HANDLERS) {
287 		LOG_ERR("Cannot accept more connections");
288 		close(client);
289 		return 0;
290 	}
291 
292 	accepted[slot] = client;
293 
294 #if defined(CONFIG_NET_IPV6)
295 	if (client_addr.sin6_family == AF_INET6) {
296 		tcp6_handler_tid[slot] = k_thread_create(
297 			&tcp6_handler_thread[slot],
298 			tcp6_handler_stack[slot],
299 			K_THREAD_STACK_SIZEOF(tcp6_handler_stack[slot]),
300 			client_conn_handler,
301 			INT_TO_POINTER(slot),
302 			&accepted[slot],
303 			&tcp6_handler_tid[slot],
304 			THREAD_PRIORITY,
305 			0, K_NO_WAIT);
306 	}
307 #endif
308 
309 #if defined(CONFIG_NET_IPV4)
310 	if (client_addr.sin6_family == AF_INET) {
311 		tcp4_handler_tid[slot] = k_thread_create(
312 			&tcp4_handler_thread[slot],
313 			tcp4_handler_stack[slot],
314 			K_THREAD_STACK_SIZEOF(tcp4_handler_stack[slot]),
315 			client_conn_handler,
316 			INT_TO_POINTER(slot),
317 			&accepted[slot],
318 			&tcp4_handler_tid[slot],
319 			THREAD_PRIORITY,
320 			0, K_NO_WAIT);
321 	}
322 #endif
323 
324 	if (LOG_LEVEL >= LOG_LEVEL_DBG) {
325 		char addr_str[INET6_ADDRSTRLEN];
326 
327 		net_addr_ntop(client_addr.sin6_family,
328 			      &client_addr.sin6_addr,
329 			      addr_str, sizeof(addr_str));
330 
331 		LOG_DBG("[%d] Connection #%d from %s",
332 			client, ++counter,
333 			addr_str);
334 	}
335 
336 	return 0;
337 }
338 
process_tcp4(void)339 static void process_tcp4(void)
340 {
341 	struct sockaddr_in addr4;
342 	int ret;
343 
344 	(void)memset(&addr4, 0, sizeof(addr4));
345 	addr4.sin_family = AF_INET;
346 	addr4.sin_port = htons(MY_PORT);
347 
348 	ret = setup(&tcp4_listen_sock, (struct sockaddr *)&addr4,
349 		    sizeof(addr4));
350 	if (ret < 0) {
351 		return;
352 	}
353 
354 	LOG_DBG("Waiting for IPv4 HTTP connections on port %d, sock %d",
355 		MY_PORT, tcp4_listen_sock);
356 
357 	while (ret == 0 || !want_to_quit) {
358 		ret = process_tcp(&tcp4_listen_sock, tcp4_accepted);
359 		if (ret < 0) {
360 			return;
361 		}
362 	}
363 }
364 
process_tcp6(void)365 static void process_tcp6(void)
366 {
367 	struct sockaddr_in6 addr6;
368 	int ret;
369 
370 	(void)memset(&addr6, 0, sizeof(addr6));
371 	addr6.sin6_family = AF_INET6;
372 	addr6.sin6_port = htons(MY_PORT);
373 
374 	ret = setup(&tcp6_listen_sock, (struct sockaddr *)&addr6,
375 		    sizeof(addr6));
376 	if (ret < 0) {
377 		return;
378 	}
379 
380 	LOG_DBG("Waiting for IPv6 HTTP connections on port %d, sock %d",
381 		MY_PORT, tcp6_listen_sock);
382 
383 	while (ret == 0 || !want_to_quit) {
384 		ret = process_tcp(&tcp6_listen_sock, tcp6_accepted);
385 		if (ret != 0) {
386 			return;
387 		}
388 	}
389 }
390 
start_listener(void)391 void start_listener(void)
392 {
393 	int i;
394 
395 	for (i = 0; i < CONFIG_NET_SAMPLE_NUM_HANDLERS; i++) {
396 #if defined(CONFIG_NET_IPV4)
397 		tcp4_accepted[i] = -1;
398 		tcp4_listen_sock = -1;
399 #endif
400 #if defined(CONFIG_NET_IPV6)
401 		tcp6_accepted[i] = -1;
402 		tcp6_listen_sock = -1;
403 #endif
404 	}
405 
406 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
407 		k_thread_start(tcp6_thread_id);
408 	}
409 
410 	if (IS_ENABLED(CONFIG_NET_IPV4)) {
411 		k_thread_start(tcp4_thread_id);
412 	}
413 }
414 
main(void)415 int main(void)
416 {
417 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
418 	int err = tls_credential_add(SERVER_CERTIFICATE_TAG,
419 				     TLS_CREDENTIAL_PUBLIC_CERTIFICATE,
420 				     server_certificate,
421 				     sizeof(server_certificate));
422 	if (err < 0) {
423 		LOG_ERR("Failed to register public certificate: %d", err);
424 	}
425 
426 	err = tls_credential_add(SERVER_CERTIFICATE_TAG,
427 				 TLS_CREDENTIAL_PRIVATE_KEY,
428 				 private_key, sizeof(private_key));
429 	if (err < 0) {
430 		LOG_ERR("Failed to register private key: %d", err);
431 	}
432 #endif
433 
434 	if (IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) {
435 		net_mgmt_init_event_callback(&mgmt_cb,
436 					     event_handler, EVENT_MASK);
437 		net_mgmt_add_event_callback(&mgmt_cb);
438 
439 		conn_mgr_mon_resend_status();
440 	}
441 
442 	if (!IS_ENABLED(CONFIG_NET_CONNECTION_MANAGER)) {
443 		/* If the config library has not been configured to start the
444 		 * app only after we have a connection, then we can start
445 		 * it right away.
446 		 */
447 		k_sem_give(&run_app);
448 	}
449 
450 	/* Wait for the connection. */
451 	k_sem_take(&run_app, K_FOREVER);
452 
453 	start_listener();
454 
455 	k_sem_take(&quit_lock, K_FOREVER);
456 
457 	if (running_status) {
458 		/* No issues, let the testing system know about this */
459 		exit(0);
460 	} else {
461 		exit(1);
462 	}
463 	return 0;
464 }
465