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