source: mainline/uspace/app/netecho/netecho.c@ 631ee0c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 631ee0c was 631ee0c, checked in by Jiri Svoboda <jiri@…>, 15 years ago

Clarify netecho description.

  • Property mode set to 100644
File size: 10.9 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup netecho
30 * @{
31 */
32
33/** @file
34 * Network echo server.
35 *
36 * Sockets-based server that echoes incomming messages. If stream mode
37 * is selected, accepts incoming connections.
38 */
39
40#include <malloc.h>
41#include <stdio.h>
42#include <str.h>
43#include <task.h>
44#include <arg_parse.h>
45
46#include <net/in.h>
47#include <net/in6.h>
48#include <net/inet.h>
49#include <net/socket.h>
50#include <net/socket_parse.h>
51
52#include "print_error.h"
53
54#define NAME "netecho"
55
56static void echo_print_help(void)
57{
58 printf(
59 "Network echo server\n" \
60 "Usage: " NAME " [options]\n" \
61 "Where options are:\n" \
62 "-b backlog | --backlog=size\n" \
63 "\tThe size of the accepted sockets queue. Only for SOCK_STREAM. The default is 3.\n" \
64 "\n" \
65 "-c count | --count=count\n" \
66 "\tThe number of received messages to handle. A negative number means infinity. The default is infinity.\n" \
67 "\n" \
68 "-f protocol_family | --family=protocol_family\n" \
69 "\tThe listenning socket protocol family. Only the PF_INET and PF_INET6 are supported.\n"
70 "\n" \
71 "-h | --help\n" \
72 "\tShow this application help.\n"
73 "\n" \
74 "-p port_number | --port=port_number\n" \
75 "\tThe port number the application should listen at. The default is 7.\n" \
76 "\n" \
77 "-r reply_string | --reply=reply_string\n" \
78 "\tThe constant reply string. The default is the original data received.\n" \
79 "\n" \
80 "-s receive_size | --size=receive_size\n" \
81 "\tThe maximum receive data size the application should accept. The default is 1024 bytes.\n" \
82 "\n" \
83 "-t socket_type | --type=socket_type\n" \
84 "\tThe listenning socket type. Only the SOCK_DGRAM and the SOCK_STREAM are supported.\n" \
85 "\n" \
86 "-v | --verbose\n" \
87 "\tShow all output messages.\n"
88 );
89}
90
91int main(int argc, char *argv[])
92{
93 size_t size = 1024;
94 int verbose = 0;
95 char *reply = NULL;
96 sock_type_t type = SOCK_DGRAM;
97 int count = -1;
98 int family = PF_INET;
99 uint16_t port = 7;
100 int backlog = 3;
101
102 socklen_t max_length = sizeof(struct sockaddr_in6);
103 uint8_t address_data[max_length];
104 struct sockaddr *address = (struct sockaddr *) address_data;
105 struct sockaddr_in *address_in = (struct sockaddr_in *) address;
106 struct sockaddr_in6 *address_in6 = (struct sockaddr_in6 *) address;
107 socklen_t addrlen;
108 char address_string[INET6_ADDRSTRLEN];
109 uint8_t *address_start;
110 int socket_id;
111 int listening_id;
112 char *data;
113 size_t length;
114 int index;
115 size_t reply_length;
116 int value;
117 int rc;
118
119 // parse the command line arguments
120 for (index = 1; index < argc; ++ index) {
121 if (argv[index][0] == '-') {
122 switch (argv[index][1]) {
123 case 'b':
124 rc = arg_parse_int(argc, argv, &index, &backlog, 0);
125 if (rc != EOK)
126 return rc;
127 break;
128 case 'c':
129 rc = arg_parse_int(argc, argv, &index, &count, 0);
130 if (rc != EOK)
131 return rc;
132 break;
133 case 'f':
134 rc = arg_parse_name_int(argc, argv, &index, &family, 0, socket_parse_protocol_family);
135 if (rc != EOK)
136 return rc;
137 break;
138 case 'h':
139 echo_print_help();
140 return EOK;
141 break;
142 case 'p':
143 rc = arg_parse_int(argc, argv, &index, &value, 0);
144 if (rc != EOK)
145 return rc;
146 port = (uint16_t) value;
147 break;
148 case 'r':
149 rc = arg_parse_string(argc, argv, &index, &reply, 0);
150 if (rc != EOK)
151 return rc;
152 break;
153 case 's':
154 rc = arg_parse_int(argc, argv, &index, &value, 0);
155 if (rc != EOK)
156 return rc;
157 size = (value >= 0) ? (size_t) value : 0;
158 break;
159 case 't':
160 rc = arg_parse_name_int(argc, argv, &index, &value, 0, socket_parse_socket_type);
161 if (rc != EOK)
162 return rc;
163 type = (sock_type_t) value;
164 break;
165 case 'v':
166 verbose = 1;
167 break;
168 // long options with the double minus sign ('-')
169 case '-':
170 if (str_lcmp(argv[index] + 2, "backlog=", 6) == 0) {
171 rc = arg_parse_int(argc, argv, &index, &backlog, 8);
172 if (rc != EOK)
173 return rc;
174 } else if (str_lcmp(argv[index] + 2, "count=", 6) == 0) {
175 rc = arg_parse_int(argc, argv, &index, &count, 8);
176 if (rc != EOK)
177 return rc;
178 } else if (str_lcmp(argv[index] + 2, "family=", 7) == 0) {
179 rc = arg_parse_name_int(argc, argv, &index, &family, 9, socket_parse_protocol_family);
180 if (rc != EOK)
181 return rc;
182 } else if (str_lcmp(argv[index] + 2, "help", 5) == 0) {
183 echo_print_help();
184 return EOK;
185 } else if (str_lcmp(argv[index] + 2, "port=", 5) == 0) {
186 rc = arg_parse_int(argc, argv, &index, &value, 7);
187 if (rc != EOK)
188 return rc;
189 port = (uint16_t) value;
190 } else if (str_lcmp(argv[index] + 2, "reply=", 6) == 0) {
191 rc = arg_parse_string(argc, argv, &index, &reply, 8);
192 if (rc != EOK)
193 return rc;
194 } else if (str_lcmp(argv[index] + 2, "size=", 5) == 0) {
195 rc = arg_parse_int(argc, argv, &index, &value, 7);
196 if (rc != EOK)
197 return rc;
198 size = (value >= 0) ? (size_t) value : 0;
199 } else if (str_lcmp(argv[index] + 2, "type=", 5) == 0) {
200 rc = arg_parse_name_int(argc, argv, &index, &value, 7, socket_parse_socket_type);
201 if (rc != EOK)
202 return rc;
203 type = (sock_type_t) value;
204 } else if (str_lcmp(argv[index] + 2, "verbose", 8) == 0) {
205 verbose = 1;
206 } else {
207 echo_print_help();
208 return EINVAL;
209 }
210 break;
211 default:
212 echo_print_help();
213 return EINVAL;
214 }
215 } else {
216 echo_print_help();
217 return EINVAL;
218 }
219 }
220
221 // check the buffer size
222 if (size <= 0) {
223 fprintf(stderr, "Receive size too small (%zu). Using 1024 bytes instead.\n", size);
224 size = 1024;
225 }
226 // size plus the terminating null (\0)
227 data = (char *) malloc(size + 1);
228 if (!data) {
229 fprintf(stderr, "Failed to allocate receive buffer.\n");
230 return ENOMEM;
231 }
232
233 // set the reply size if set
234 reply_length = reply ? str_length(reply) : 0;
235
236 // prepare the address buffer
237 bzero(address_data, max_length);
238 switch (family) {
239 case PF_INET:
240 address_in->sin_family = AF_INET;
241 address_in->sin_port = htons(port);
242 addrlen = sizeof(struct sockaddr_in);
243 break;
244 case PF_INET6:
245 address_in6->sin6_family = AF_INET6;
246 address_in6->sin6_port = htons(port);
247 addrlen = sizeof(struct sockaddr_in6);
248 break;
249 default:
250 fprintf(stderr, "Protocol family is not supported\n");
251 return EAFNOSUPPORT;
252 }
253
254 // get a listening socket
255 listening_id = socket(family, type, 0);
256 if (listening_id < 0) {
257 socket_print_error(stderr, listening_id, "Socket create: ", "\n");
258 return listening_id;
259 }
260
261 // if the stream socket is used
262 if (type == SOCK_STREAM) {
263 // check the backlog
264 if (backlog <= 0) {
265 fprintf(stderr, "Accepted sockets queue size too small (%zu). Using 3 instead.\n", size);
266 backlog = 3;
267 }
268 // set the backlog
269 rc = listen(listening_id, backlog);
270 if (rc != EOK) {
271 socket_print_error(stderr, rc, "Socket listen: ", "\n");
272 return rc;
273 }
274 }
275
276 // bind the listenning socket
277 rc = bind(listening_id, address, addrlen);
278 if (rc != EOK) {
279 socket_print_error(stderr, rc, "Socket bind: ", "\n");
280 return rc;
281 }
282
283 if (verbose)
284 printf("Socket %d listenning at %d\n", listening_id, port);
285
286 socket_id = listening_id;
287
288 // do count times
289 // or indefinitely if set to a negative value
290 while (count) {
291
292 addrlen = max_length;
293 if (type == SOCK_STREAM) {
294 // acceept a socket if the stream socket is used
295 socket_id = accept(listening_id, address, &addrlen);
296 if (socket_id <= 0) {
297 socket_print_error(stderr, socket_id, "Socket accept: ", "\n");
298 } else {
299 if (verbose)
300 printf("Socket %d accepted\n", socket_id);
301 }
302 }
303
304 // if the datagram socket is used or the stream socked was accepted
305 if (socket_id > 0) {
306
307 // receive an echo request
308 value = recvfrom(socket_id, data, size, 0, address, &addrlen);
309 if (value < 0) {
310 socket_print_error(stderr, value, "Socket receive: ", "\n");
311 } else {
312 length = (size_t) value;
313 if (verbose) {
314 // print the header
315
316 // get the source port and prepare the address buffer
317 address_start = NULL;
318 switch (address->sa_family) {
319 case AF_INET:
320 port = ntohs(address_in->sin_port);
321 address_start = (uint8_t *) &address_in->sin_addr.s_addr;
322 break;
323 case AF_INET6:
324 port = ntohs(address_in6->sin6_port);
325 address_start = (uint8_t *) &address_in6->sin6_addr.s6_addr;
326 break;
327 default:
328 fprintf(stderr, "Address family %u (%#x) is not supported.\n",
329 address->sa_family, address->sa_family);
330 }
331 // parse the source address
332 if (address_start) {
333 rc = inet_ntop(address->sa_family, address_start, address_string, sizeof(address_string));
334 if (rc != EOK) {
335 fprintf(stderr, "Received address error %d\n", rc);
336 } else {
337 data[length] = '\0';
338 printf("Socket %d received %zu bytes from %s:%d\n%s\n",
339 socket_id, length, address_string, port, data);
340 }
341 }
342 }
343
344 // answer the request either with the static reply or the original data
345 rc = sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen);
346 if (rc != EOK)
347 socket_print_error(stderr, rc, "Socket send: ", "\n");
348 }
349
350 // close the accepted stream socket
351 if (type == SOCK_STREAM) {
352 rc = closesocket(socket_id);
353 if (rc != EOK)
354 socket_print_error(stderr, rc, "Close socket: ", "\n");
355 }
356
357 }
358
359 // decrease the count if positive
360 if (count > 0) {
361 count--;
362 if (verbose)
363 printf("Waiting for next %d packet(s)\n", count);
364 }
365 }
366
367 if (verbose)
368 printf("Closing the socket\n");
369
370 // close the listenning socket
371 rc = closesocket(listening_id);
372 if (rc != EOK) {
373 socket_print_error(stderr, rc, "Close socket: ", "\n");
374 return rc;
375 }
376
377 if (verbose)
378 printf("Exiting\n");
379
380 return EOK;
381}
382
383/** @}
384 */
Note: See TracBrowser for help on using the repository browser.