1 /*
2  * Copyright (c) 2017 Linaro Limited
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 #include <stdbool.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 
12 #if !defined(__ZEPHYR__)
13 
14 #include <netinet/in.h>
15 #include <sys/socket.h>
16 #include <sys/select.h>
17 #include <arpa/inet.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 
21 /* Generic read()/write() is available in POSIX config, so use it. */
22 #define READ(fd, buf, sz) read(fd, buf, sz)
23 #define WRITE(fd, buf, sz) write(fd, buf, sz)
24 
25 #else
26 
27 #include <zephyr/posix/netinet/in.h>
28 #include <zephyr/posix/sys/socket.h>
29 #include <zephyr/posix/arpa/inet.h>
30 #include <zephyr/posix/unistd.h>
31 #include <zephyr/posix/fcntl.h>
32 #include <zephyr/posix/sys/select.h>
33 #include <zephyr/net/socket.h>
34 #include <zephyr/kernel.h>
35 
36 /* Generic read()/write() are not defined, so use socket-specific recv(). */
37 #define READ(fd, buf, sz) recv(fd, buf, sz, 0)
38 #define WRITE(fd, buf, sz) send(fd, buf, sz, 0)
39 
40 #endif
41 
42 /* For Zephyr, keep max number of fd's in sync with max poll() capacity */
43 #ifdef CONFIG_ZVFS_POLL_MAX
44 #define NUM_FDS CONFIG_ZVFS_POLL_MAX
45 #else
46 #define NUM_FDS 5
47 #endif
48 
49 #define BIND_PORT 4242
50 
51 /* Number of simultaneous client connections will be NUM_FDS be minus 2 */
52 fd_set readfds;
53 fd_set workfds;
54 int fdnum;
55 
56 #define fatal(msg, ...) { \
57 		printf("Error: " msg "\n", ##__VA_ARGS__); \
58 		exit(1); \
59 	}
60 
61 
setblocking(int fd,bool val)62 static void setblocking(int fd, bool val)
63 {
64 	int fl, res;
65 
66 	fl = fcntl(fd, F_GETFL, 0);
67 	if (fl == -1) {
68 		fatal("fcntl(F_GETFL): %d", errno);
69 	}
70 
71 	if (val) {
72 		fl &= ~O_NONBLOCK;
73 	} else {
74 		fl |= O_NONBLOCK;
75 	}
76 
77 	res = fcntl(fd, F_SETFL, fl);
78 	if (fl == -1) {
79 		fatal("fcntl(F_SETFL): %d", errno);
80 	}
81 }
82 
pollfds_add(int fd)83 int pollfds_add(int fd)
84 {
85 	FD_SET(fd, &readfds);
86 
87 	if (fd >= fdnum) {
88 		fdnum = fd + 1;
89 	}
90 
91 	return 0;
92 }
93 
pollfds_del(int fd)94 void pollfds_del(int fd)
95 {
96 	FD_CLR(fd, &readfds);
97 }
98 
main(void)99 int main(void)
100 {
101 	int res;
102 	static int counter;
103 #if !defined(CONFIG_SOC_SERIES_CC32XX)
104 	int serv4;
105 	struct sockaddr_in bind_addr4 = {
106 		.sin_family = AF_INET,
107 		.sin_port = htons(BIND_PORT),
108 		.sin_addr = {
109 			.s_addr = htonl(INADDR_ANY),
110 		},
111 	};
112 #endif
113 	int serv6;
114 	struct sockaddr_in6 bind_addr6 = {
115 		.sin6_family = AF_INET6,
116 		.sin6_port = htons(BIND_PORT),
117 		.sin6_addr = IN6ADDR_ANY_INIT,
118 	};
119 
120 	FD_ZERO(&readfds);
121 #if !defined(CONFIG_SOC_SERIES_CC32XX)
122 	serv4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
123 	if (serv4 < 0) {
124 		printf("error: socket: %d\n", errno);
125 		exit(1);
126 	}
127 
128 	res = bind(serv4, (struct sockaddr *)&bind_addr4, sizeof(bind_addr4));
129 	if (res == -1) {
130 		printf("Cannot bind IPv4, errno: %d\n", errno);
131 	}
132 
133 	setblocking(serv4, false);
134 	listen(serv4, 5);
135 	pollfds_add(serv4);
136 #endif
137 	serv6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
138 	if (serv6 < 0) {
139 		printf("error: socket(AF_INET6): %d\n", errno);
140 		exit(1);
141 	}
142 	#ifdef IPV6_V6ONLY
143 	/* For Linux, we need to make socket IPv6-only to bind it to the
144 	 * same port as IPv4 socket above.
145 	 */
146 	int TRUE = 1;
147 	res = setsockopt(serv6, IPPROTO_IPV6, IPV6_V6ONLY, &TRUE, sizeof(TRUE));
148 	if (res < 0) {
149 		printf("error: setsockopt: %d\n", errno);
150 		exit(1);
151 	}
152 	#endif
153 	res = bind(serv6, (struct sockaddr *)&bind_addr6, sizeof(bind_addr6));
154 	if (res == -1) {
155 		printf("Cannot bind IPv6, errno: %d\n", errno);
156 	}
157 
158 	setblocking(serv6, false);
159 	listen(serv6, 5);
160 	pollfds_add(serv6);
161 
162 	printf("Async select-based TCP echo server waits for connections on "
163 	       "port %d...\n", BIND_PORT);
164 
165 	while (1) {
166 		struct sockaddr_storage client_addr;
167 		socklen_t client_addr_len = sizeof(client_addr);
168 		char addr_str[32];
169 
170 		/* As select overwrites passed fd_set, we run it against
171 		 * temporary fd_set, by first copying a real fd_set into it.
172 		 */
173 		workfds = readfds;
174 		res = select(fdnum, &workfds, NULL, NULL, NULL);
175 		if (res == -1) {
176 			printf("select error: %d\n", errno);
177 			continue;
178 		}
179 
180 		for (int i = 0; i < fdnum; i++) {
181 			if (!FD_ISSET(i, &workfds)) {
182 				continue;
183 			}
184 			int fd = i;
185 #if defined(CONFIG_SOC_SERIES_CC32XX)
186 			/*
187 			 * On TI CC32xx, the same port cannot be bound to two
188 			 * different sockets. Instead, the IPv6 socket is used
189 			 * to handle both IPv4 and IPv6 connections.
190 			 */
191 			if (fd == serv6) {
192 #else
193 			if (fd == serv4 || fd == serv6) {
194 #endif
195 				/* If server socket */
196 				int client = accept(fd, (struct sockaddr *)&client_addr,
197 						    &client_addr_len);
198 				void *addr = &((struct sockaddr_in *)&client_addr)->sin_addr;
199 
200 				inet_ntop(client_addr.ss_family, addr,
201 					  addr_str, sizeof(addr_str));
202 				printf("Connection #%d from %s fd=%d\n", counter++,
203 				       addr_str, client);
204 				if (pollfds_add(client) < 0) {
205 					static char msg[] = "Too many connections\n";
206 					WRITE(client, msg, sizeof(msg) - 1);
207 					close(client);
208 				} else {
209 					setblocking(client, false);
210 				}
211 			} else {
212 				char buf[128];
213 				int len = READ(fd, buf, sizeof(buf));
214 				if (len <= 0) {
215 					if (len < 0) {
216 						printf("error: RECV: %d\n",
217 						       errno);
218 					}
219 error:
220 					pollfds_del(fd);
221 					close(fd);
222 					printf("Connection fd=%d closed\n", fd);
223 				} else {
224 					int out_len;
225 					const char *p;
226 					/* We implement semi-async server,
227 					 * where reads are async, but writes
228 					 * *can* be sync (blocking). Note that
229 					 * in majority of cases they expected
230 					 * to not block, but to be robust, we
231 					 * handle all possibilities.
232 					 */
233 					setblocking(fd, true);
234 
235 					for (p = buf; len; len -= out_len) {
236 						out_len = WRITE(fd, p, len);
237 						if (out_len < 0) {
238 							printf("error: "
239 							       "WRITE: %d\n",
240 							       errno);
241 							goto error;
242 						}
243 						p += out_len;
244 					}
245 
246 					setblocking(fd, false);
247 				}
248 			}
249 		}
250 	}
251 	return 0;
252 }
253