chargen.c
Go to the documentation of this file.
1 /**
2  * @file chargen.c
3  * @brief Character generator protocol
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @section Description
26  *
27  * The character generator service is a useful debugging and measurement
28  * tool. The service simply sends data until the calling user terminates
29  * the connection. Refer to RFC 864 for complete details
30  *
31  * @author Oryx Embedded SARL (www.oryx-embedded.com)
32  * @version 1.9.0
33  **/
34 
35 //Switch to the appropriate trace level
36 #define TRACE_LEVEL STD_SERVICES_TRACE_LEVEL
37 
38 //Dependencies
39 #include "core/net.h"
40 #include "std_services/chargen.h"
41 #include "debug.h"
42 
43 //Character pattern (from RFC 864)
44 const char_t pattern[190] =
45 {
46  '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3',
47  '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F',
48  'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
49  'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
50  'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', ' ',
51  '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3',
52  '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F',
53  'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
54  'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
55  'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', ' '
56 };
57 
58 
59 /**
60  * @brief Start TCP chargen service
61  * @return Error code
62  **/
63 
65 {
66  error_t error;
67  Socket *socket;
68  OsTask *task;
69 
70  //Debug message
71  TRACE_INFO("Starting TCP chargen service...\r\n");
72 
73  //Open a TCP socket
75  //Failed to open socket?
76  if(socket == NULL)
77  return ERROR_OPEN_FAILED;
78 
79  //Start of exception handling block
80  do
81  {
82  //Bind the newly created socket to port 19
84  //Failed to bind the socket to the desired port?
85  if(error)
86  break;
87 
88  //Place the socket into listening mode
89  error = socketListen(socket, 0);
90  //Any error to report?
91  if(error)
92  break;
93 
94  //Create a task to handle incoming connection requests
95  task = osCreateTask("TCP Chargen Listener", tcpChargenListenerTask,
97 
98  //Unable to create the task?
99  if(task == OS_INVALID_HANDLE)
100  {
101  //Report an error to the calling function
102  error = ERROR_OUT_OF_RESOURCES;
103  break;
104  }
105 
106  //End of exception handling block
107  } while(0);
108 
109  //Any error to report?
110  if(error)
111  {
112  //Clean up side effects...
114  }
115 
116  //Return status code
117  return error;
118 }
119 
120 
121 /**
122  * @brief Task handling connection requests
123  * @param[in] param Pointer to the chargen service context
124  **/
125 
126 void tcpChargenListenerTask(void *param)
127 {
128  error_t error;
129  uint16_t clientPort;
130  IpAddr clientIpAddr;
131  Socket *serverSocket;
132  Socket *clientSocket;
133  ChargenServiceContext *context;
134  OsTask *task;
135 
136  //Point to the listening socket
137  serverSocket = (Socket *) param;
138 
139  //Main loop
140  while(1)
141  {
142  //Accept an incoming connection
143  clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort);
144  //Check whether a valid connection request has been received
145  if(!clientSocket)
146  continue;
147 
148  //Debug message
149  TRACE_INFO("Chargen service: connection established with client %s port %" PRIu16 "\r\n",
150  ipAddrToString(&clientIpAddr, NULL), clientPort);
151 
152  //Adjust timeout
153  error = socketSetTimeout(clientSocket, CHARGEN_TIMEOUT);
154 
155  //Any error to report?
156  if(error)
157  {
158  //Close socket
159  socketClose(clientSocket);
160  //Wait for an incoming connection attempt
161  continue;
162  }
163 
164  //Allocate resources for the new connection
165  context = osAllocMem(sizeof(ChargenServiceContext));
166 
167  //Failed to allocate memory?
168  if(context == NULL)
169  {
170  //Close socket
171  socketClose(clientSocket);
172  //Wait for an incoming connection attempt
173  continue;
174  }
175 
176  //Record the handle of the newly created socket
177  context->socket = clientSocket;
178 
179  //Create a task to service the current connection
180  task = osCreateTask("TCP Chargen Connection", tcpChargenConnectionTask,
182 
183  //Did we encounter an error?
184  if(task == OS_INVALID_HANDLE)
185  {
186  //Close socket
187  socketClose(clientSocket);
188  //Release resources
189  osFreeMem(context);
190  }
191  }
192 }
193 
194 
195 /**
196  * @brief TCP chargen service implementation
197  * @param[in] param Pointer to the chargen service context
198  **/
199 
200 void tcpChargenConnectionTask(void *param)
201 {
202  error_t error;
203  //size_t i;
204  size_t n;
205  //size_t offset;
206  size_t byteCount;
207  systime_t startTime;
209  ChargenServiceContext *context;
210 
211  //Get a pointer to the context
212  context = (ChargenServiceContext *) param;
213  //Get current time
214  startTime = osGetSystemTime();
215 
216  //Initialize counters
217  byteCount = 0;
218  //offset = 0;
219 
220  //Once a connection is established a stream of data is sent out
221  //the connection (and any data received is thrown away). This
222  //continues until the calling user terminates the connection
223  while(1)
224  {
225  //Format output data
226  /*for(i = 0; i < CHARGEN_BUFFER_SIZE; i += 95)
227  {
228  //Calculate the length of the current line
229  n = MIN(CHARGEN_BUFFER_SIZE - i, 95);
230  //Copy character pattern
231  memcpy(context->buffer + i, pattern + offset, n);
232  }
233 
234  //Update offset
235  offset += CHARGEN_BUFFER_SIZE + 95 - i;
236  //Wrap around if necessary
237  if(offset >= 95) offset = 0;*/
238 
239  //Send data
240  error = socketSend(context->socket, context->buffer, CHARGEN_BUFFER_SIZE, &n, 0);
241  //Any error to report?
242  if(error)
243  break;
244 
245  //Total number of bytes sent
246  byteCount += n;
247  }
248 
249  //Graceful shutdown
251  //Compute total duration
252  duration = osGetSystemTime() - startTime;
253  //Avoid division by zero...
254  if(!duration)
255  duration = 1;
256 
257  //Debug message
258  TRACE_INFO("Chargen service: %" PRIuSIZE " bytes "
259  "sent in %" PRIu32 " ms (%" PRIu32 " kBps, %" PRIu32 " kbps)\r\n",
261 
262  //Close socket
263  socketClose(context->socket);
264  //Release previously allocated memory
265  osFreeMem(context);
266 
267  //Kill ourselves
268  osDeleteTask(NULL);
269 }
270 
271 
272 /**
273  * @brief Start UDP chargen service
274  * @return Error code
275  **/
276 
278 {
279  error_t error;
280  ChargenServiceContext *context;
281  OsTask *task;
282 
283  //Debug message
284  TRACE_INFO("Starting UDP chargen service...\r\n");
285 
286  //Allocate a memory block to hold the context
287  context = osAllocMem(sizeof(ChargenServiceContext));
288  //Failed to allocate memory?
289  if(context == NULL)
290  return ERROR_OUT_OF_MEMORY;
291 
292  //Start of exception handling block
293  do
294  {
295  //Open a UDP socket
297 
298  //Failed to open socket?
299  if(context->socket == NULL)
300  {
301  //Report an error
302  error = ERROR_OPEN_FAILED;
303  //Exit immediately
304  break;
305  }
306 
307  //The server listens for incoming datagrams on port 19
308  error = socketBind(context->socket, &IP_ADDR_ANY, CHARGEN_PORT);
309  //Unable to bind the socket to the desired port?
310  if(error)
311  break;
312 
313  //Create a task to handle incoming datagrams
314  task = osCreateTask("UDP Chargen", udpChargenTask,
316 
317  //Unable to create the task?
318  if(task == OS_INVALID_HANDLE)
319  {
320  //Report an error to the calling function
321  error = ERROR_OUT_OF_RESOURCES;
322  break;
323  }
324 
325  //End of exception handling block
326  } while(0);
327 
328  //Any error to report?
329  if(error)
330  {
331  //Clean up side effects...
332  socketClose(context->socket);
333  osFreeMem(context);
334  }
335 
336  //Return status code
337  return error;
338 }
339 
340 
341 /**
342  * @brief UDP chargen service implementation
343  * @param[in] param Pointer to the chargen service context
344  **/
345 
346 void udpChargenTask(void *param)
347 {
348  error_t error;
349  size_t i;
350  size_t k;
351  size_t n;
352  size_t length;
353  uint16_t port;
354  IpAddr ipAddr;
355  ChargenServiceContext *context;
356 
357  //Get a pointer to the context
358  context = (ChargenServiceContext *) param;
359 
360  //Main loop
361  while(1)
362  {
363  //Wait for an incoming datagram
364  error = socketReceiveFrom(context->socket, &ipAddr, &port,
365  context->buffer, CHARGEN_BUFFER_SIZE, &n, 0);
366 
367  //Any datagram received?
368  if(!error)
369  {
370  //When a datagram is received, an answering datagram is sent
371  //containing a random number (between 0 and 512) of characters
372  length = netGetRand() % 513;
373 
374  //Reset line counter
375  n = 0;
376 
377  //Format output data
378  for(i = 0; i < length; i += 74)
379  {
380  //Calculate the length of the current line
381  k = MIN(length - i, 74);
382  //Copy character pattern
383  memcpy(context->buffer + i, pattern + n, k);
384 
385  //End each line with carriage return and line feed
386  if(k == 74)
387  {
388  context->buffer[i + 72] = '\r';
389  context->buffer[i + 73] = '\n';
390  }
391 
392  //Increment line counter
393  if(++n >= 95)
394  n = 0;
395  }
396 
397  //Send data to the remote host
398  error = socketSendTo(context->socket, &ipAddr, port,
399  context->buffer, length, &n, 0);
400 
401  //Debug message
402  TRACE_INFO("Chargen service: %" PRIuSIZE " bytes sent to %s port %" PRIu16 "\r\n",
403  n, ipAddrToString(&ipAddr, NULL), port);
404  }
405  }
406 }
error_t udpChargenStart(void)
Start UDP chargen service.
Definition: chargen.c:277
uint32_t systime_t
Definition: compiler_port.h:44
char char_t
Definition: compiler_port.h:41
systime_t osGetSystemTime(void)
Retrieve system time.
void osFreeMem(void *p)
Release a previously allocated memory block.
TCP/IP stack core.
Debugging facilities.
OsTask * osCreateTask(const char_t *name, OsTaskCode taskCode, void *param, size_t stackSize, int_t priority)
Create a new task.
Socket * socketAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
Permit an incoming connection attempt on a socket.
Definition: socket.c:457
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:420
IP network address.
Definition: ip.h:57
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:797
char_t * ipAddrToString(const IpAddr *ipAddr, char_t *str)
Convert a binary IP address to a string representation.
Definition: ip.c:685
const IpAddr IP_ADDR_ANY
Definition: ip.c:42
#define CHARGEN_SERVICE_STACK_SIZE
Definition: chargen.h:38
uint16_t duration
error_t socketReceiveFrom(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, void *data, size_t size, size_t *received, uint_t flags)
Receive a datagram from a connectionless socket.
Definition: socket.c:604
Chargen service context.
Definition: chargen.h:75
#define CHARGEN_SERVICE_PRIORITY
Definition: chargen.h:45
int_t socket(int_t family, int_t type, int_t protocol)
Create a socket that is bound to a specific transport service provider.
Definition: bsd_socket.c:106
void udpChargenTask(void *param)
UDP chargen service implementation.
Definition: chargen.c:346
uint8_t ipAddr[4]
Definition: mib_common.h:185
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:216
#define Socket
Definition: socket.h:34
uint32_t netGetRand(void)
Get a random value.
Definition: net.c:1523
Task object.
#define CHARGEN_BUFFER_SIZE
Definition: chargen.h:50
void tcpChargenListenerTask(void *param)
Task handling connection requests.
Definition: chargen.c:126
#define MIN(a, b)
Definition: os_port.h:60
error_t socketShutdown(Socket *socket, uint_t how)
Disable reception, transmission, or both.
Definition: socket.c:760
#define TRACE_INFO(...)
Definition: debug.h:86
void * osAllocMem(size_t size)
Allocate a memory block.
error_t
Error codes.
Definition: error.h:40
const char_t pattern[190]
Definition: chargen.c:44
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:92
#define PRIuSIZE
Definition: compiler_port.h:72
#define CHARGEN_PORT
Definition: chargen.h:63
void osDeleteTask(OsTask *task)
Delete a task.
void tcpChargenConnectionTask(void *param)
TCP chargen service implementation.
Definition: chargen.c:200
uint16_t port
Definition: dns_common.h:221
char_t buffer[CHARGEN_BUFFER_SIZE]
Definition: chargen.h:78
uint16_t byteCount
Character generator protocol.
#define OS_INVALID_HANDLE
Definition: os_port.h:77
#define CHARGEN_TIMEOUT
Definition: chargen.h:57
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:331
uint8_t length
Definition: dtls_misc.h:140
uint8_t n
error_t tcpChargenStart(void)
Start TCP chargen service.
Definition: chargen.c:64
error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, const void *data, size_t length, size_t *written, uint_t flags)
Send a datagram to a specific destination.
Definition: socket.c:511
error_t socketSend(Socket *socket, const void *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: socket.c:490