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