]> begriffs open source - cmsis-driver-validation/blob - Tools/SockServer/Embedded/MDK/Source/SockServer.c
Update GitHub Actions runner to ubuntu-22.04 (#18)
[cmsis-driver-validation] / Tools / SockServer / Embedded / MDK / Source / SockServer.c
1 /*
2  * Copyright (c) 2019-2020 Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
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
9  *
10  * www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  * -----------------------------------------------------------------------------
19  *
20  * Project:     SockServer
21  * Title:       SockServer embedded system 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]
26  *
27  * -----------------------------------------------------------------------------
28  */
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include "rl_net.h"
34 #include "cmsis_os2.h"
35 #include "SockServer.h"
36
37 // Global status variables
38 SOCKADDR_IN remote_addr;        // Remote IP address and port
39 uint32_t rx_cnt;                // Receive count
40 uint32_t tx_cnt;                // Transmit count
41
42 // Local functions
43 static void EchoThread (void *argument);
44 static void ChargenThread (void *argument);
45 static void DiscardThread (void *argument);
46 static char gen_char (char *buf, char setchar, uint32_t len);
47
48 // Generate character array for transmit
49 static char gen_char (char *buf, char setchar, uint32_t len) {
50   uint32_t i;
51   char ch;
52
53   if ((++setchar < 0x21) || (setchar == 0x7f)) {
54     setchar = 0x21;
55   }
56   for (i = 0, ch = setchar; i < (len-2); i++) {
57     buf[i] = ch;
58     if (++ch == 0x7f) ch = 0x21;
59   }
60   buf[i]   = '\n';
61   buf[i+1] = '\r';
62   return (setchar);
63 }
64
65 // Datagram server thread
66 // (runs ECHO and CHARGEN services)
67 void DgramServer (void *argument) {
68   SOCKADDR_IN sa;
69   int32_t sock_echo,sock_chargen;
70   int32_t nfds,rc,sa_len;
71   char *buff,setchar;
72   struct timeval tv;
73   fd_set fds;
74
75   // Allocate sockets
76   sock_echo    = socket (PF_INET, SOCK_DGRAM, 0);
77   sock_chargen = socket (PF_INET, SOCK_DGRAM, 0);
78
79   // Bind sockets
80   sa.sin_family      = AF_INET;
81   sa.sin_addr.s_addr = INADDR_ANY;
82
83   sa.sin_port        = htons (ECHO_PORT);
84   bind (sock_echo, (SOCKADDR *)&sa, sizeof(sa));
85
86   sa.sin_port        = htons (CHARGEN_PORT);
87   bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
88
89   setchar = '@';
90   buff = malloc (BUFF_SIZE);
91
92   // Receive data
93   for (;;) {
94     FD_ZERO(&fds);
95     FD_SET(sock_echo, &fds);
96     FD_SET(sock_chargen, &fds);
97     nfds = (sock_echo > sock_chargen) ? sock_echo : sock_chargen;
98     tv.tv_sec  = 120;
99     tv.tv_usec = 0;
100
101     // Wait for the packet
102     select (nfds+1, &fds, 0, 0, &tv);
103
104     if (FD_ISSET(sock_echo, &fds)) {
105       // Data ready, recvfrom will not block
106       sa_len = sizeof (sa);
107       rc = recvfrom (sock_echo, buff, BUFF_SIZE, 0, (SOCKADDR *)&sa, &sa_len);
108       if (rc > 0) {
109         rx_cnt += rc;
110         memcpy (&remote_addr, &sa, sizeof(sa));
111         rc = sendto (sock_echo, buff, rc, 0, (SOCKADDR *)&sa, sa_len);
112         if (rc > 0) tx_cnt += rc;
113       }
114       if (rc < 0) {
115         break;
116       }
117     }
118     if (FD_ISSET(sock_chargen, &fds)) {
119       // Data ready, recvfrom will not block
120       sa_len = sizeof (sa);
121       rc = recvfrom (sock_chargen, buff, BUFF_SIZE, 0, (SOCKADDR *)&sa, &sa_len);
122       if (rc > 0) {
123         rx_cnt += rc;
124         memcpy (&remote_addr, &sa, sizeof(sa));
125         int32_t len = rand() >> 22;
126         if (len < 2)         len = 2;
127         if (len > BUFF_SIZE) len = BUFF_SIZE;
128         setchar = gen_char (buff, setchar, len);
129         rc = sendto (sock_chargen, buff, len, 0, (SOCKADDR *)&sa, sa_len);
130         if (rc > 0) tx_cnt += rc;
131       }
132       if (rc < 0) {
133         break;
134       }
135     }
136   }
137   free (buff);
138 }
139
140 // ECHO stream socket handler (2 instances)
141 static void EchoThread (void *argument) {
142   int32_t sock = (int32_t)argument;
143   int32_t rc;
144
145   char *buff = malloc (BUFF_SIZE);
146   for (; buff;) {
147     rc = recv (sock, buff, BUFF_SIZE, 0);
148     if (rc > 0) {
149       rx_cnt += rc;
150       rc = send (sock, buff, rc, 0);
151       if (rc > 0) tx_cnt += rc;
152       // ESC terminates the thread
153       if (buff[0] == ESC) break;
154     }
155     if (rc < 0) break;
156   }
157   closesocket (sock);
158   free (buff);
159 }
160
161 // CHARGEN stream socket handler (2 instances)
162 static void ChargenThread (void *argument) {
163   int32_t rc,sock = (int32_t)argument;
164   char buff[82],setchar = '@';
165
166   for (;;) {
167     rc = recv (sock, buff, sizeof(buff), MSG_DONTWAIT);
168     if (rc > 0) rx_cnt += rc;
169     // ESC terminates the thread
170     if ((rc > 0) && (buff[0] == ESC)) break;
171     setchar = gen_char (buff, setchar, 81);
172     rc = send (sock, buff, 81, 0);
173     if (rc < 0) break;
174     else tx_cnt += rc;
175     osDelay (100);
176   }
177   closesocket (sock);
178 }
179
180 // DISCARD stream socket handler (1 instance)
181 static void DiscardThread (void *argument) {
182   int32_t rc,sock = (int32_t)argument;
183   char buff[40];
184
185   for (;;) {
186     rc = recv (sock, buff, sizeof(buff), 0);
187     if (rc > 0) rx_cnt += rc;
188     // ESC terminates the thread
189     if ((rc > 0) && (buff[0] == ESC)) break;
190     if (rc < 0) break;
191   }
192   closesocket (sock);
193 }
194
195 // Stream server thread
196 // (runs ECHO, CHARGEN and DISCARD services)
197 void StreamServer (void *argument) {
198   SOCKADDR_IN sa;
199   int32_t sock_echo,sock_chargen,sock_discard,sock_timeout;
200   int32_t sock,nfds,sa_len;
201   struct timeval tv;
202   fd_set fds;
203
204   // Allocate sockets
205   sock_echo    = socket (PF_INET, SOCK_STREAM, 0);
206   sock_chargen = socket (PF_INET, SOCK_STREAM, 0);
207   sock_discard = socket (PF_INET, SOCK_STREAM, 0);
208   sock_timeout = socket (PF_INET, SOCK_STREAM, 0);
209
210   // Bind sockets
211   sa.sin_family      = AF_INET;
212   sa.sin_addr.s_addr = INADDR_ANY;
213
214   sa.sin_port = htons (ECHO_PORT);
215   bind (sock_echo,    (SOCKADDR *)&sa, sizeof(sa));
216
217   sa.sin_port = htons (CHARGEN_PORT);
218   bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
219
220   sa.sin_port = htons (DISCARD_PORT);
221   bind (sock_discard, (SOCKADDR *)&sa, sizeof(sa));
222
223   sa.sin_port = htons (TCP_TIMEOUT_PORT);
224   bind (sock_timeout, (SOCKADDR *)&sa, sizeof(sa));
225
226   // Start listening
227   listen (sock_echo, 2);
228   listen (sock_chargen, 2);
229   listen (sock_discard, 1);
230   listen (sock_timeout, 1);
231
232   for (;;) {
233     FD_ZERO(&fds);
234     FD_SET(sock_echo, &fds);
235     FD_SET(sock_chargen, &fds);
236     FD_SET(sock_discard, &fds);
237
238     nfds = sock_echo;
239     if (sock_chargen > nfds) nfds = sock_chargen;
240     if (sock_discard > nfds) nfds = sock_discard;
241
242     tv.tv_sec  = 120;
243     tv.tv_usec = 0;
244
245     // Wait for the client to connect
246     select (nfds+1, &fds, 0, 0, &tv);
247     if (FD_ISSET(sock_echo, &fds)) {
248       // Connect is pending, accept will not block
249       sa_len = sizeof(sa);
250       sock   = accept (sock_echo, (SOCKADDR *)&sa, &sa_len);
251       if (sock >= 0) {
252         memcpy (&remote_addr, &sa, sa_len);
253         // Create spawn thread (max.2)
254         osThreadNew(EchoThread, (void *)sock, NULL);
255       }
256     }
257     if (FD_ISSET(sock_chargen, &fds)) {
258       // Connect is pending, accept will not block
259       sa_len = sizeof(sa);
260       sock   = accept (sock_chargen, (SOCKADDR *)&sa, &sa_len);
261       if (sock >= 0) {
262         memcpy (&remote_addr, &sa, sa_len);
263         // Create spawn thread (max.2)
264         osThreadNew(ChargenThread, (void *)sock, NULL);
265       }
266     }
267     if (FD_ISSET(sock_discard, &fds)) {
268       // Connect is pending, accept will not block
269       sa_len = sizeof(sa);
270       sock   = accept (sock_discard, (SOCKADDR *)&sa, &sa_len);
271       if (sock >= 0) {
272         memcpy (&remote_addr, &sa, sa_len);
273         // Create spawn thread (max.1)
274         osThreadNew(DiscardThread, (void *)sock, NULL);
275       }
276     }
277     osDelay (10);
278   }
279 }
280
281 // Test assistant thread
282 void TestAssistant (void *argument) {
283   SOCKADDR_IN sa;
284   int32_t sock,sd,rc,sa_len;
285   static char buff[1500];
286
287   while (1) {
288     // Create socket
289     sock = socket (PF_INET, SOCK_STREAM, 0);
290
291     // Server mode first
292     sa.sin_family      = AF_INET;
293     sa.sin_addr.s_addr = INADDR_ANY;
294     sa.sin_port        = htons (ASSISTANT_PORT);
295     bind (sock, (SOCKADDR *)&sa, sizeof(sa));
296     listen (sock, 1);
297
298     while (1) {
299       // Wait for the client to connect
300       sa_len = sizeof (sa);
301       sd = accept (sock, (SOCKADDR *)&sa, &sa_len);
302       if (sd >= 0) {
303         // Set blocking receive timeout
304         uint32_t tout = 2000;
305         setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tout, sizeof(tout));
306         // Receive the command (tout = 2s)
307         rc = recv (sd, buff, sizeof(buff), 0);
308         if (rc > 0) {
309           buff[rc] = 0;
310           if ((strncmp (buff, "CONNECT TCP", 11) == 0) ||
311               (strncmp (buff, "CONNECT UDP", 11) == 0) ||
312               (strncmp (buff, "SEND TCP", 8) == 0)     ||
313               (strncmp (buff, "RECV TCP", 8) == 0)) {
314             break;
315           }
316         }
317         closesocket (sd);
318         osDelay (10);
319       }
320     }
321     closesocket (sock);
322
323     /* Syntax:  CONNECT <proto>,<ip_addr>,<port>,<delay_ms>
324        Param:   <proto>    = protocol (TCP, UDP)
325                 <ip_addr>  = IP address (0.0.0.0 = sender address)
326                 <port>     = port number
327                 <delay_ms> = startup delay
328
329        Example: CONNECT TCP,192.168.1.200,80,600
330        (wait 600ms then connect to 192.168.1.200, port 80)
331     */
332     if (buff[0] == 'C') { // CONNECT
333       uint16_t delay,port;
334       IN_ADDR  da;
335
336       closesocket (sd);
337
338       da.s_addr = INADDR_ANY;
339       sscanf (buff+11,",%hhu.%hhu.%hhu.%hhu,%hu,%hu",
340                        &da.s_b1, &da.s_b2, &da.s_b3, &da.s_b4, &port, &delay);
341       if (da.s_addr != INADDR_ANY) {
342         // Supplied address not 0.0.0.0 use it
343         sa.sin_addr.s_addr = da.s_addr;
344       }
345       sa.sin_port = htons (port);
346
347       // Limit the timeout
348       if (delay < 10)   delay = 10;
349       if (delay > 5000) delay = 6000;
350       osDelay (delay);
351
352       // Create stream or datagram socket
353       sock = socket (PF_INET, (buff[8] == 'T') ? SOCK_STREAM : SOCK_DGRAM, 0);
354
355       // Connect to requested address
356       rc = connect (sock, (SOCKADDR *)&sa, sa_len);
357       if (rc == 0) {
358         // Send some text, wait and close
359         send (sock, "SockServer", 10, 0);
360         osDelay (500);
361       }
362       closesocket (sock);
363       osDelay (10);
364       continue;
365     }
366
367     /* Syntax:  SEND <proto>,<bsize>,<time_ms>
368        Param:   <proto>   = protocol (TCP, UDP)
369                 <bsize>   = size of data block in bytes 
370                 <time_ms> = test duration in ms
371     */
372     if (buff[0] == 'S') { // SEND
373       uint32_t bsize,time,ticks;
374       int32_t  i,n,cnt,ch = 'a';
375     
376       // Parse command parameters
377       sscanf (buff+8,",%u,%u",&bsize,&time);
378     
379       // Check limits
380       if (bsize < 32)   bsize = 32;
381       if (bsize > 1460) bsize = 1460;
382       if (time < 500)   time  = 500;
383       if (time > 60000) time  = 60000;
384
385       osDelay (10);
386
387       time  = SYSTICK_MSEC(time);
388       ticks = GET_SYSTICK();
389       i = cnt = 0;
390       do {
391         n = sprintf (buff,"Block[%d] ",++i);
392         memset (buff+n, ch, bsize-n);
393         buff[bsize] = 0;
394         if (++ch > '~') ch = ' ';
395         n = send (sd, buff, bsize, 0);
396         if (n > 0) cnt += n; 
397       } while (GET_SYSTICK() - ticks < time);
398
399       // Inform the client of the number of bytes received
400       n = sprintf (buff,"STAT %d bytes.",cnt);
401       send (sd, buff, n, 0);
402
403       // Let the client close the connection
404       while (recv (sd, buff, sizeof(buff), 0) > 0);
405
406       closesocket (sd);
407       continue;
408     }
409
410     /* Syntax:  RECV <proto>,<bsize>
411        Param:   <proto> = protocol (TCP, UDP)
412                 <bsize> = size of data block in bytes 
413     */
414     if (buff[0] == 'R') { // RECV
415       uint32_t bsize;
416       int32_t  n,cnt;
417     
418       // Parse command parameters
419       sscanf (buff+8,",%u",&bsize);
420     
421       // Check limits
422       if (bsize < 32)   bsize = 32;
423       if (bsize > 1460) bsize = 1460;
424
425       osDelay (10);
426
427       for (cnt = 0;  ; cnt += n) {
428         n = recv (sd, buff, bsize, 0);
429         if (strncmp(buff, "STOP", 4) == 0) {
430           // Client terminated upload
431           break;
432         }
433         if (n <= 0) break; 
434       }
435
436       // Inform the client of the number of bytes received
437       n = sprintf (buff, "STAT %d bytes.",cnt);
438       send (sd, buff, n, 0);
439  
440       // Let the client close the connection
441       while (recv (sd, buff, sizeof(buff), 0) > 0);
442
443       closesocket (sd);
444       continue;
445     }
446   }
447 }