]> begriffs open source - cmsis-driver-validation/blob - Tools/SockServer/PC/Win/Source/SockServer.c
Update GitHub Actions runner to ubuntu-22.04 (#18)
[cmsis-driver-validation] / Tools / SockServer / PC / Win / 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 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]
26  *
27  * -----------------------------------------------------------------------------
28  */
29
30 #define VERSION     "v1.1"
31
32 #include <stdio.h>
33 #include <stdint.h>
34 #include <conio.h>
35 #include <time.h>
36 #include <windows.h>
37 #include <winsock2.h>
38 #include "SockServer.h"
39
40 // Link with ws2_32.lib
41 #pragma comment(lib, "Ws2_32.lib")
42
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
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 // Debug print status
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);
68 }
69
70 // Datagram server thread
71 // (runs ECHO and CHARGEN services)
72 DWORD WINAPI DgramServer (void *argument) {
73   SOCKADDR_IN sa;
74   int32_t sock_echo,sock_chargen;
75   int32_t nfds,rc,sa_len;
76   char *buff,setchar;
77   struct timeval tv;
78   fd_set fds;
79
80   // Allocate sockets
81   sock_echo    = socket (PF_INET, SOCK_DGRAM, 0);
82   sock_chargen = socket (PF_INET, SOCK_DGRAM, 0);
83
84   // Bind sockets
85   sa.sin_family      = AF_INET;
86   sa.sin_addr.s_addr = INADDR_ANY;
87
88   sa.sin_port        = htons (ECHO_PORT);
89   bind (sock_echo, (SOCKADDR *)&sa, sizeof(sa));
90
91   sa.sin_port        = htons (CHARGEN_PORT);
92   bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
93
94   setchar = '@';
95   buff = malloc (BUFF_SIZE);
96
97   // Receive data
98   for (;;) {
99     FD_ZERO(&fds);
100     FD_SET(sock_echo, &fds);
101     FD_SET(sock_chargen, &fds);
102
103     nfds = sock_echo;
104     if (sock_chargen > nfds) nfds = sock_chargen;
105
106     tv.tv_sec  = 120;
107     tv.tv_usec = 0;
108
109     // Wait for the packet
110     select (nfds+1, &fds, 0, 0, &tv);
111
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);
116       if (rc > 0) {
117         rx_cnt += rc;
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;
121         print_status ();
122       }
123       if (rc < 0) {
124         break;
125       }
126     }
127
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);
132       if (rc > 0) {
133         rx_cnt += rc;
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;
141         print_status ();
142       }
143       if (rc < 0) {
144         break;
145       }
146     }
147   }
148   free (buff);
149   return (0);
150 }
151
152 // ECHO stream socket handler
153 DWORD WINAPI EchoThread (void *argument) {
154   int32_t sock = (int32_t)argument;
155   int32_t rc;
156
157   char *buff = malloc (BUFF_SIZE);
158   for (; buff;) {
159     rc = recv (sock, buff, BUFF_SIZE, 0);
160     if (rc <= 0) break;
161     rx_cnt += rc;
162     rc = send (sock, buff, rc, 0);
163     if (rc < 0) break;
164     tx_cnt += rc;
165     // ESC terminates the thread
166     if (buff[0] == ESC) break;
167     print_status ();
168   }
169   closesocket (sock);
170   free (buff);
171   return (0);
172 }
173
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;  
179
180   // Set non-blocking mode
181   ioctlsocket (sock, FIONBIO, &enable);
182   for (;;) {
183     rc = recv (sock, buff, sizeof(buff), 0);
184     if (rc > 0) {
185       rx_cnt += rc;
186       // ESC terminates the thread
187       if (buff[0] == ESC) break;
188     }
189     else {
190       // Non-blocking mode, check error code
191       if (WSAGetLastError() != WSAEWOULDBLOCK) break;
192     }
193     setchar = gen_char (buff, setchar, 81);
194     rc = send (sock, buff, 81, 0);
195     if (rc < 0) break;
196     tx_cnt += rc;
197     print_status ();
198     Sleep (100);
199   }
200   closesocket (sock);
201   return (0);
202 }
203
204 // DISCARD stream socket handler
205 DWORD WINAPI DiscardThread (void *argument) {
206   int32_t rc,sock = (int32_t)argument;
207   char buff[40];
208   
209   for (;;) {
210     rc = recv (sock, buff, sizeof(buff), 0);
211     if (rc <= 0) break;
212     rx_cnt += rc;
213     // ESC terminates the thread
214     if (buff[0] == ESC) break;
215     print_status ();
216   }
217   closesocket (sock);
218   return (0);
219 }
220
221 // Test assistant thread
222 DWORD WINAPI AssistantThread (void *argument) {
223   int32_t rc,sock = (int32_t)argument;
224   SOCKADDR_IN sa;
225   int32_t sa_len;
226   char buff[1500];
227    
228   // Set blocking receive timeout
229   uint32_t tout = 2000;
230   setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tout, sizeof(tout));
231
232   // Get remote peer address
233   sa_len = sizeof (sa);
234   getpeername (sock, (SOCKADDR *)&sa, &sa_len);
235
236   // Receive the command (tout = 2s)
237   rc = recv (sock, buff, sizeof(buff), 0);
238   if (rc <= 0) {
239     closesocket (sock);
240     return (0);
241   }
242
243   // Parse the command
244   buff[rc] = 0;
245
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)
249               <port>     = port number
250               <delay_ms> = startup delay
251
252      Example: CONNECT TCP,192.168.1.200,80,600
253      (wait 600ms then connect to 192.168.1.200, port 80)
254   */
255   if ((strncmp (buff, "CONNECT TCP", 11) == 0) || 
256       (strncmp (buff, "CONNECT UDP", 11) == 0)) {
257     uint16_t port;
258     uint32_t delay;
259     IN_ADDR da;
260
261     Sleep (10);
262     closesocket (sock);
263     
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);
268  
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;
274     }
275  
276     // Limit the timeout
277     if (delay < 10)   delay = 10;
278     if (delay > 5000) delay = 5000;
279     Sleep (delay);
280
281     // Create stream or datagram socket
282     sock = socket (PF_INET,  (buff[8] == 'T') ? SOCK_STREAM : SOCK_DGRAM, 0);
283
284     // Connect to requested address
285     rc = connect (sock, (SOCKADDR *)&sa, sizeof(sa));
286     if (rc == 0) {
287       // Send some text, wait and close
288       send (sock, "SockServer", 10, 0);
289       Sleep (500);
290     }
291     closesocket (sock);
292     return (0);
293   }
294
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
299   */
300   if (strncmp (buff, "SEND TCP", 8) == 0) { 
301     uint32_t bsize,time;
302     clock_t ticks,tout;
303     int32_t i,n,cnt,ch = 'a';
304     
305     // Parse command parameters
306     sscanf (buff+8,",%u,%u",&bsize,&time);
307     
308     // Check limits
309     if (bsize < 32)    bsize = 32;
310     if (bsize > 1460)  bsize = 1460;
311     if (time < 500)    time  = 500;
312     if (time > 60000)  time  = 60000;
313
314     // Adjust Winsock2 send buffering
315     n = bsize * 2;
316     setsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char *)&n, sizeof(n));
317     Sleep (10);
318
319     i = cnt = 0;
320     ticks = clock ();
321     do {
322       n = sprintf (buff,"Block[%d] ",++i);
323       memset (buff+n, ch, bsize-n);
324       buff[bsize] = 0;
325       if (++ch > '~') ch = ' ';
326       n = send (sock, buff, bsize, 0);
327       if (n > 0) cnt += n; 
328     } while (clock () - ticks < time);
329
330     // Inform the client of the number of bytes received
331     n = sprintf (buff,"STAT %d bytes.",cnt);       
332     send (sock, buff, n, 0);
333
334     // let the client close the connection
335     while (recv (sock, buff, sizeof(buff), 0) > 0);
336
337     closesocket (sock);
338     return (0);
339   }
340
341   /* Syntax:  RECV <proto>,<bsize>
342      Param:   <proto> = protocol (TCP, UDP)
343               <bsize> = size of data block in bytes 
344   */
345   if (strncmp (buff, "RECV TCP", 8) == 0) { 
346     uint32_t bsize;
347     int32_t i,n,cnt;
348     
349     // Parse command parameters
350     sscanf (buff+8,",%u",&bsize);
351     
352     // Check limits
353     if (bsize < 32)    bsize = 32;
354     if (bsize > 1460)  bsize = 1460;
355
356     Sleep (10);
357
358     for (cnt = 0;  ; cnt += n) {
359       n = recv (sock, buff, bsize, 0);
360       if (strncmp(buff, "STOP", 4) == 0) {
361         // Client terminated upload
362         break;
363       }
364       if (n <= 0) break; 
365     }
366
367     // Inform the client of the number of bytes received
368     n = sprintf (buff, "STAT %d bytes.",cnt);
369     send (sock, buff, n, 0);
370  
371     // let the client close the connection
372     while (recv (sock, buff, sizeof(buff), 0) > 0);
373
374     closesocket (sock);
375     return (0);
376   }
377
378   closesocket (sock);
379   return (0);
380 }
381
382 // Conditional accept filtering (Winsock2)
383 int32_t CALLBACK ConditionAcceptFunc(
384     LPWSABUF lpCallerId,
385     LPWSABUF lpCallerData,
386     LPQOS pQos,
387     LPQOS lpGQOS,
388     LPWSABUF lpCalleeId,
389     LPWSABUF lpCalleeData,
390     GROUP FAR * g,
391     DWORD_PTR dwCallbackData) {
392   return (CF_REJECT);
393 }
394
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;
399   SOCKADDR_IN sa;
400   int32_t sock,nfds,sa_len;
401   struct timeval tv;
402   fd_set fds;
403   uint32_t en;
404   int32_t retv;
405
406   // Allocate sockets
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);
412   
413   // Enable conditional accept (Winsock2)
414   en = 1;
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");
418   }
419   
420   // Bind sockets
421   sa.sin_family      = AF_INET;
422   sa.sin_addr.s_addr = INADDR_ANY;
423
424   sa.sin_port = htons (ECHO_PORT);
425   bind (sock_echo,    (SOCKADDR *)&sa, sizeof(sa));
426
427   sa.sin_port = htons (CHARGEN_PORT);
428   bind (sock_chargen, (SOCKADDR *)&sa, sizeof(sa));
429
430   sa.sin_port = htons (DISCARD_PORT);
431   bind (sock_discard, (SOCKADDR *)&sa, sizeof(sa));
432
433   sa.sin_port = htons  (ASSISTANT_PORT);
434   bind (sock_assistant,(SOCKADDR *)&sa, sizeof(sa));
435
436   sa.sin_port = htons  (TCP_REJECTED_PORT);
437   bind (sock_rejected, (SOCKADDR *)&sa, sizeof(sa));
438   
439   // Start listening
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);
445   
446   for (;;) {
447     FD_ZERO(&fds);
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);
453     
454     nfds = sock_echo;
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;
459     
460     tv.tv_sec  = 120;
461     tv.tv_usec = 0;
462
463     // Wait for the client to connect
464     select (nfds+1, &fds, 0, 0, &tv);
465
466     if (FD_ISSET(sock_echo, &fds)) {
467       // Connect is pending, accept will not block
468       sa_len = sizeof(sa);
469       sock   = accept (sock_echo, (SOCKADDR *)&sa, &sa_len);
470       if (sock >= 0) {
471         memcpy (&remote_addr, &sa, sa_len);
472         // Create spawn thread
473         CreateThread(NULL, 0, EchoThread, (void *)sock, 0, NULL);
474       }
475     }
476
477     if (FD_ISSET(sock_chargen, &fds)) {
478       // Connect is pending, accept will not block
479       sa_len = sizeof(sa);
480       sock   = accept (sock_chargen, (SOCKADDR *)&sa, &sa_len);
481       if (sock >= 0) {
482         memcpy (&remote_addr, &sa, sa_len);
483         // Create spawn thread
484         CreateThread(NULL, 0, ChargenThread, (void *)sock, 0, NULL);
485       }
486     }
487
488     if (FD_ISSET(sock_discard, &fds)) {
489       // Connect is pending, accept will not block
490       sa_len = sizeof(sa);
491       sock   = accept (sock_discard, (SOCKADDR *)&sa, &sa_len);
492       if (sock >= 0) {
493         memcpy (&remote_addr, &sa, sa_len);
494         // Create spawn thread
495         CreateThread(NULL, 0, DiscardThread, (void *)sock, 0, NULL);
496       }
497     }
498
499     if (FD_ISSET(sock_assistant, &fds)) {
500       // Connect is pending, accept will not block
501       sa_len = sizeof(sa);
502       sock   = accept (sock_assistant, (SOCKADDR *)&sa, &sa_len);
503       if (sock >= 0) {
504         memcpy (&remote_addr, &sa, sa_len);
505         // Create spawn thread
506         CreateThread(NULL, 0, AssistantThread, (void *)sock, 0, NULL);
507       }
508     }
509
510     if (FD_ISSET(sock_rejected, &fds)) {
511       // Connect is pending, reject it
512       sock = WSAAccept (sock_rejected, NULL, NULL, &ConditionAcceptFunc, 0);
513       if (sock >= 0) {
514         closesocket (sock);
515       }
516     }
517     Sleep (10);
518   }
519 }
520
521 // Main program
522 int main() {
523   WSADATA wsaData;
524   HANDLE thread;
525   int iResult;
526   char ac[80];
527   struct hostent *phe;
528     
529   printf("\nSockServer %s\n", VERSION);
530
531   // Initialize Winsock2 
532   iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
533   if (iResult != NO_ERROR) {
534     printf("WSAStartup failed with error: %d\n", iResult);
535     return (1);
536   }
537
538   thread = CreateThread(NULL, 0, DgramServer, NULL, 0, NULL);
539   if (thread == NULL) {
540     printf("Failed to create thread: DgramServer\n");
541     WSACleanup();
542     return (1);
543   }
544
545   thread = CreateThread(NULL, 0, StreamServer, NULL, 0, NULL);
546   if (thread == NULL) {
547     printf("Failed to create thread: StreamServer\n");
548     WSACleanup();
549     return (1);
550   }
551
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);
556     if (phe != NULL) {
557       for (int i = 0; phe->h_addr_list[i] != 0; i++) {
558         struct in_addr addr;
559         memcpy (&addr, phe->h_addr_list[i], sizeof (struct in_addr));
560         printf ("Address: %s\n", inet_ntoa(addr));
561       }
562     }
563   }
564
565   printf("\nPress any key to stop...\n");
566   _getch ();
567   
568   // Terminate use of Winsock2
569   WSACleanup();
570   printf ("\nOk\n");
571   return 0;
572 }