2 * Copyright (c) 2019-2020 Arm Limited. All rights reserved.
4 * SPDX-License-Identifier: Apache-2.0
6 * Licensed under the Apache License, Version 2.0 (the License); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * -----------------------------------------------------------------------------
21 * Title: SockServer PC application
22 * Purpose: Implements ECHO, DISCARD and CHARGEN services
23 * - Echo Protocol service [RFC 862]
24 * - Discard Protocol service [RFC 863]
25 * - Character Generator Protocol service [RFC 864]
27 * -----------------------------------------------------------------------------
30 #define VERSION "v1.1"
38 #include "SockServer.h"
40 // Link with ws2_32.lib
41 #pragma comment(lib, "Ws2_32.lib")
43 // Global status variables
44 SOCKADDR_IN remote_addr; // Remote IP address and port
45 uint32_t rx_cnt; // Receive count
46 uint32_t tx_cnt; // Transmit count
48 // Generate character array for transmit
49 static char gen_char (char *buf, char setchar, uint32_t len) {
53 if ((++setchar < 0x21) || (setchar == 0x7f)) {
56 for (i = 0, ch = setchar; i < (len-2); i++) {
58 if (++ch == 0x7f) ch = 0x21;
66 static void print_status (void) {
67 printf("\rAddr=%s, rx_cnt=%d, tx_cnt=%d",inet_ntoa(remote_addr.sin_addr),rx_cnt,tx_cnt);
70 // Datagram server thread
71 // (runs ECHO and CHARGEN services)
72 DWORD WINAPI DgramServer (void *argument) {
74 int32_t sock_echo,sock_chargen;
75 int32_t nfds,rc,sa_len;
81 sock_echo = socket (PF_INET, SOCK_DGRAM, 0);
82 sock_chargen = socket (PF_INET, SOCK_DGRAM, 0);
85 sa.sin_family = AF_INET;
86 sa.sin_addr.s_addr = INADDR_ANY;
88 sa.sin_port = htons (ECHO_PORT);
89 bind (sock_echo, (SOCKADDR *)&sa, sizeof(sa));
91 sa.sin_port = htons (CHARGEN_PORT);
92 bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
95 buff = malloc (BUFF_SIZE);
100 FD_SET(sock_echo, &fds);
101 FD_SET(sock_chargen, &fds);
104 if (sock_chargen > nfds) nfds = sock_chargen;
109 // Wait for the packet
110 select (nfds+1, &fds, 0, 0, &tv);
112 if (FD_ISSET(sock_echo, &fds)) {
113 // Data ready, recvfrom will not block
114 sa_len = sizeof (sa);
115 rc = recvfrom (sock_echo, buff, BUFF_SIZE, 0, (SOCKADDR *)&sa, &sa_len);
118 memcpy (&remote_addr, &sa, sizeof(sa));
119 rc = sendto (sock_echo, buff, rc, 0, (SOCKADDR *)&sa, sa_len);
120 if (rc > 0) tx_cnt += rc;
128 if (FD_ISSET(sock_chargen, &fds)) {
129 // Data ready, recvfrom will not block
130 sa_len = sizeof (sa);
131 rc = recvfrom (sock_chargen, buff, BUFF_SIZE, 0, (SOCKADDR *)&sa, &sa_len);
134 memcpy (&remote_addr, &sa, sizeof(sa));
135 int32_t len = rand() >> 22;
136 if (len < 2) len = 2;
137 if (len > BUFF_SIZE) len = BUFF_SIZE;
138 setchar = gen_char (buff, setchar, len);
139 rc = sendto (sock_chargen, buff, len, 0, (SOCKADDR *)&sa, sa_len);
140 if (rc > 0) tx_cnt += rc;
152 // ECHO stream socket handler
153 DWORD WINAPI EchoThread (void *argument) {
154 int32_t sock = (int32_t)argument;
157 char *buff = malloc (BUFF_SIZE);
159 rc = recv (sock, buff, BUFF_SIZE, 0);
162 rc = send (sock, buff, rc, 0);
165 // ESC terminates the thread
166 if (buff[0] == ESC) break;
174 // CHARGEN stream socket handler
175 DWORD WINAPI ChargenThread (void *argument) {
176 int32_t rc,sock = (int32_t)argument;
177 char buff[82],setchar = '@';
178 unsigned long enable = 1;
180 // Set non-blocking mode
181 ioctlsocket (sock, FIONBIO, &enable);
183 rc = recv (sock, buff, sizeof(buff), 0);
186 // ESC terminates the thread
187 if (buff[0] == ESC) break;
190 // Non-blocking mode, check error code
191 if (WSAGetLastError() != WSAEWOULDBLOCK) break;
193 setchar = gen_char (buff, setchar, 81);
194 rc = send (sock, buff, 81, 0);
204 // DISCARD stream socket handler
205 DWORD WINAPI DiscardThread (void *argument) {
206 int32_t rc,sock = (int32_t)argument;
210 rc = recv (sock, buff, sizeof(buff), 0);
213 // ESC terminates the thread
214 if (buff[0] == ESC) break;
221 // Test assistant thread
222 DWORD WINAPI AssistantThread (void *argument) {
223 int32_t rc,sock = (int32_t)argument;
228 // Set blocking receive timeout
229 uint32_t tout = 2000;
230 setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tout, sizeof(tout));
232 // Get remote peer address
233 sa_len = sizeof (sa);
234 getpeername (sock, (SOCKADDR *)&sa, &sa_len);
236 // Receive the command (tout = 2s)
237 rc = recv (sock, buff, sizeof(buff), 0);
246 /* Syntax: CONNECT <proto>,<ip_addr>,<port>,<delay_ms>
247 Param: <proto> = protocol (TCP, UDP)
248 <ip_addr> = IP address (0.0.0.0 = sender address)
250 <delay_ms> = startup delay
252 Example: CONNECT TCP,192.168.1.200,80,600
253 (wait 600ms then connect to 192.168.1.200, port 80)
255 if ((strncmp (buff, "CONNECT TCP", 11) == 0) ||
256 (strncmp (buff, "CONNECT UDP", 11) == 0)) {
264 // Parse command parameters
265 da.s_addr = INADDR_ANY;
266 sscanf (buff+11,",%hhu.%hhu.%hhu.%hhu,%hu,%u",
267 &da.s_b1,&da.s_b2,&da.s_b3,&da.s_b4,&port,&delay);
269 sa.sin_family = AF_INET;
270 sa.sin_port = htons (port);
271 if (da.s_addr != INADDR_ANY) {
272 // Supplied address not 0.0.0.0 use it
273 sa.sin_addr.s_addr = da.s_addr;
277 if (delay < 10) delay = 10;
278 if (delay > 5000) delay = 5000;
281 // Create stream or datagram socket
282 sock = socket (PF_INET, (buff[8] == 'T') ? SOCK_STREAM : SOCK_DGRAM, 0);
284 // Connect to requested address
285 rc = connect (sock, (SOCKADDR *)&sa, sizeof(sa));
287 // Send some text, wait and close
288 send (sock, "SockServer", 10, 0);
295 /* Syntax: SEND <proto>,<bsize>,<time_ms>
296 Param: <proto> = protocol (TCP, UDP)
297 <bsize> = size of data block in bytes
298 <time_ms> = test duration in ms
300 if (strncmp (buff, "SEND TCP", 8) == 0) {
303 int32_t i,n,cnt,ch = 'a';
305 // Parse command parameters
306 sscanf (buff+8,",%u,%u",&bsize,&time);
309 if (bsize < 32) bsize = 32;
310 if (bsize > 1460) bsize = 1460;
311 if (time < 500) time = 500;
312 if (time > 60000) time = 60000;
314 // Adjust Winsock2 send buffering
316 setsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char *)&n, sizeof(n));
322 n = sprintf (buff,"Block[%d] ",++i);
323 memset (buff+n, ch, bsize-n);
325 if (++ch > '~') ch = ' ';
326 n = send (sock, buff, bsize, 0);
328 } while (clock () - ticks < time);
330 // Inform the client of the number of bytes received
331 n = sprintf (buff,"STAT %d bytes.",cnt);
332 send (sock, buff, n, 0);
334 // let the client close the connection
335 while (recv (sock, buff, sizeof(buff), 0) > 0);
341 /* Syntax: RECV <proto>,<bsize>
342 Param: <proto> = protocol (TCP, UDP)
343 <bsize> = size of data block in bytes
345 if (strncmp (buff, "RECV TCP", 8) == 0) {
349 // Parse command parameters
350 sscanf (buff+8,",%u",&bsize);
353 if (bsize < 32) bsize = 32;
354 if (bsize > 1460) bsize = 1460;
358 for (cnt = 0; ; cnt += n) {
359 n = recv (sock, buff, bsize, 0);
360 if (strncmp(buff, "STOP", 4) == 0) {
361 // Client terminated upload
367 // Inform the client of the number of bytes received
368 n = sprintf (buff, "STAT %d bytes.",cnt);
369 send (sock, buff, n, 0);
371 // let the client close the connection
372 while (recv (sock, buff, sizeof(buff), 0) > 0);
382 // Conditional accept filtering (Winsock2)
383 int32_t CALLBACK ConditionAcceptFunc(
385 LPWSABUF lpCallerData,
389 LPWSABUF lpCalleeData,
391 DWORD_PTR dwCallbackData) {
395 // Stream server thread
396 // (runs ECHO, CHARGEN, DISCARD and ASSISTANT services)
397 DWORD WINAPI StreamServer (void *argument) {
398 int32_t sock_echo,sock_chargen,sock_discard,sock_assistant,sock_rejected;
400 int32_t sock,nfds,sa_len;
407 sock_echo = socket (PF_INET, SOCK_STREAM, 0);
408 sock_chargen = socket (PF_INET, SOCK_STREAM, 0);
409 sock_discard = socket (PF_INET, SOCK_STREAM, 0);
410 sock_assistant = socket (PF_INET, SOCK_STREAM, 0);
411 sock_rejected = socket (PF_INET, SOCK_STREAM, 0);
413 // Enable conditional accept (Winsock2)
415 retv = setsockopt (sock_rejected, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (char *)&en, sizeof(en));
416 if (retv != NO_ERROR) {
417 printf ("Failed to set SO_CONDITIONAL_ACCEPT\n");
421 sa.sin_family = AF_INET;
422 sa.sin_addr.s_addr = INADDR_ANY;
424 sa.sin_port = htons (ECHO_PORT);
425 bind (sock_echo, (SOCKADDR *)&sa, sizeof(sa));
427 sa.sin_port = htons (CHARGEN_PORT);
428 bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
430 sa.sin_port = htons (DISCARD_PORT);
431 bind (sock_discard, (SOCKADDR *)&sa, sizeof(sa));
433 sa.sin_port = htons (ASSISTANT_PORT);
434 bind (sock_assistant,(SOCKADDR *)&sa, sizeof(sa));
436 sa.sin_port = htons (TCP_REJECTED_PORT);
437 bind (sock_rejected, (SOCKADDR *)&sa, sizeof(sa));
440 listen (sock_echo, 2);
441 listen (sock_chargen, 2);
442 listen (sock_discard, 2);
443 listen (sock_assistant, 2);
444 listen (sock_rejected, 2);
448 FD_SET(sock_echo, &fds);
449 FD_SET(sock_chargen, &fds);
450 FD_SET(sock_discard, &fds);
451 FD_SET(sock_assistant, &fds);
452 FD_SET(sock_rejected, &fds);
455 if (sock_chargen > nfds) nfds = sock_chargen;
456 if (sock_discard > nfds) nfds = sock_discard;
457 if (sock_assistant > nfds) nfds = sock_assistant;
458 if (sock_rejected > nfds) nfds = sock_rejected;
463 // Wait for the client to connect
464 select (nfds+1, &fds, 0, 0, &tv);
466 if (FD_ISSET(sock_echo, &fds)) {
467 // Connect is pending, accept will not block
469 sock = accept (sock_echo, (SOCKADDR *)&sa, &sa_len);
471 memcpy (&remote_addr, &sa, sa_len);
472 // Create spawn thread
473 CreateThread(NULL, 0, EchoThread, (void *)sock, 0, NULL);
477 if (FD_ISSET(sock_chargen, &fds)) {
478 // Connect is pending, accept will not block
480 sock = accept (sock_chargen, (SOCKADDR *)&sa, &sa_len);
482 memcpy (&remote_addr, &sa, sa_len);
483 // Create spawn thread
484 CreateThread(NULL, 0, ChargenThread, (void *)sock, 0, NULL);
488 if (FD_ISSET(sock_discard, &fds)) {
489 // Connect is pending, accept will not block
491 sock = accept (sock_discard, (SOCKADDR *)&sa, &sa_len);
493 memcpy (&remote_addr, &sa, sa_len);
494 // Create spawn thread
495 CreateThread(NULL, 0, DiscardThread, (void *)sock, 0, NULL);
499 if (FD_ISSET(sock_assistant, &fds)) {
500 // Connect is pending, accept will not block
502 sock = accept (sock_assistant, (SOCKADDR *)&sa, &sa_len);
504 memcpy (&remote_addr, &sa, sa_len);
505 // Create spawn thread
506 CreateThread(NULL, 0, AssistantThread, (void *)sock, 0, NULL);
510 if (FD_ISSET(sock_rejected, &fds)) {
511 // Connect is pending, reject it
512 sock = WSAAccept (sock_rejected, NULL, NULL, &ConditionAcceptFunc, 0);
529 printf("\nSockServer %s\n", VERSION);
531 // Initialize Winsock2
532 iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
533 if (iResult != NO_ERROR) {
534 printf("WSAStartup failed with error: %d\n", iResult);
538 thread = CreateThread(NULL, 0, DgramServer, NULL, 0, NULL);
539 if (thread == NULL) {
540 printf("Failed to create thread: DgramServer\n");
545 thread = CreateThread(NULL, 0, StreamServer, NULL, 0, NULL);
546 if (thread == NULL) {
547 printf("Failed to create thread: StreamServer\n");
552 // Print info about local host
553 if (gethostname (ac, sizeof(ac)) != SOCKET_ERROR) {
554 printf ("\nServer name: %s\n",ac);
555 phe = gethostbyname (ac);
557 for (int i = 0; phe->h_addr_list[i] != 0; i++) {
559 memcpy (&addr, phe->h_addr_list[i], sizeof (struct in_addr));
560 printf ("Address: %s\n", inet_ntoa(addr));
565 printf("\nPress any key to stop...\n");
568 // Terminate use of Winsock2