source: mainline/uspace/app/netecho/netecho.c@ 774e9ecd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 774e9ecd was a8e5051, checked in by Jakub Jermar <jakub@…>, 15 years ago

Gently cleanup netecho.

  • Property mode set to 100644
File size: 10.5 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 application.
35 * Answers received packets.
36 */
37
38#include <malloc.h>
39#include <stdio.h>
40#include <str.h>
41#include <task.h>
42#include <arg_parse.h>
43#include <err.h>
44
45#include <net/in.h>
46#include <net/in6.h>
47#include <net/inet.h>
48#include <net/socket.h>
49#include <net/socket_parse.h>
50
51#include "print_error.h"
52
53/** Network echo module name. */
54#define NAME "Network Echo"
55
56static void echo_print_help(void)
57{
58 printf(
59 "Network Echo aplication\n" \
60 "Usage: echo [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 ERROR_DECLARE;
94
95 size_t size = 1024;
96 int verbose = 0;
97 char *reply = NULL;
98 sock_type_t type = SOCK_DGRAM;
99 int count = -1;
100 int family = PF_INET;
101 uint16_t port = 7;
102 int backlog = 3;
103
104 socklen_t max_length = sizeof(struct sockaddr_in6);
105 uint8_t address_data[max_length];
106 struct sockaddr *address = (struct sockaddr *) address_data;
107 struct sockaddr_in *address_in = (struct sockaddr_in *) address;
108 struct sockaddr_in6 *address_in6 = (struct sockaddr_in6 *) address;
109 socklen_t addrlen;
110 char address_string[INET6_ADDRSTRLEN];
111 uint8_t *address_start;
112 int socket_id;
113 int listening_id;
114 char *data;
115 size_t length;
116 int index;
117 size_t reply_length;
118 int value;
119
120 // parse the command line arguments
121 for (index = 1; index < argc; ++ index) {
122 if (argv[index][0] == '-') {
123 switch (argv[index][1]) {
124 case 'b':
125 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &backlog, 0));
126 break;
127 case 'c':
128 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &count, 0));
129 break;
130 case 'f':
131 ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &family, 0, socket_parse_protocol_family));
132 break;
133 case 'h':
134 echo_print_help();
135 return EOK;
136 break;
137 case 'p':
138 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 0));
139 port = (uint16_t) value;
140 break;
141 case 'r':
142 ERROR_PROPAGATE(arg_parse_string(argc, argv, &index, &reply, 0));
143 break;
144 case 's':
145 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 0));
146 size = (value >= 0) ? (size_t) value : 0;
147 break;
148 case 't':
149 ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &value, 0, socket_parse_socket_type));
150 type = (sock_type_t) value;
151 break;
152 case 'v':
153 verbose = 1;
154 break;
155 // long options with the double minus sign ('-')
156 case '-':
157 if (str_lcmp(argv[index] + 2, "backlog=", 6) == 0) {
158 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &backlog, 8));
159 } else if (str_lcmp(argv[index] + 2, "count=", 6) == 0) {
160 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &count, 8));
161 } else if (str_lcmp(argv[index] + 2, "family=", 7) == 0) {
162 ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &family, 9, socket_parse_protocol_family));
163 } else if (str_lcmp(argv[index] + 2, "help", 5) == 0) {
164 echo_print_help();
165 return EOK;
166 } else if (str_lcmp(argv[index] + 2, "port=", 5) == 0) {
167 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 7));
168 port = (uint16_t) value;
169 } else if (str_lcmp(argv[index] + 2, "reply=", 6) == 0) {
170 ERROR_PROPAGATE(arg_parse_string(argc, argv, &index, &reply, 8));
171 } else if (str_lcmp(argv[index] + 2, "size=", 5) == 0) {
172 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 7));
173 size = (value >= 0) ? (size_t) value : 0;
174 } else if (str_lcmp(argv[index] + 2, "type=", 5) == 0) {
175 ERROR_PROPAGATE(arg_parse_name_int(argc, argv, &index, &value, 7, socket_parse_socket_type));
176 type = (sock_type_t) value;
177 } else if (str_lcmp(argv[index] + 2, "verbose", 8) == 0) {
178 verbose = 1;
179 } else {
180 echo_print_help();
181 return EINVAL;
182 }
183 break;
184 default:
185 echo_print_help();
186 return EINVAL;
187 }
188 } else {
189 echo_print_help();
190 return EINVAL;
191 }
192 }
193
194 // check the buffer size
195 if (size <= 0) {
196 fprintf(stderr, "Receive size too small (%d). Using 1024 bytes instead.\n", size);
197 size = 1024;
198 }
199 // size plus the terminating null (\0)
200 data = (char *) malloc(size + 1);
201 if (!data) {
202 fprintf(stderr, "Failed to allocate receive buffer.\n");
203 return ENOMEM;
204 }
205
206 // set the reply size if set
207 reply_length = reply ? str_length(reply) : 0;
208
209 // prepare the address buffer
210 bzero(address_data, max_length);
211 switch (family) {
212 case PF_INET:
213 address_in->sin_family = AF_INET;
214 address_in->sin_port = htons(port);
215 addrlen = sizeof(struct sockaddr_in);
216 break;
217 case PF_INET6:
218 address_in6->sin6_family = AF_INET6;
219 address_in6->sin6_port = htons(port);
220 addrlen = sizeof(struct sockaddr_in6);
221 break;
222 default:
223 fprintf(stderr, "Protocol family is not supported\n");
224 return EAFNOSUPPORT;
225 }
226
227 // get a listening socket
228 listening_id = socket(family, type, 0);
229 if (listening_id < 0) {
230 socket_print_error(stderr, listening_id, "Socket create: ", "\n");
231 return listening_id;
232 }
233
234 // if the stream socket is used
235 if (type == SOCK_STREAM) {
236 // check the backlog
237 if (backlog <= 0) {
238 fprintf(stderr, "Accepted sockets queue size too small (%d). Using 3 instead.\n", size);
239 backlog = 3;
240 }
241 // set the backlog
242 if (ERROR_OCCURRED(listen(listening_id, backlog))) {
243 socket_print_error(stderr, ERROR_CODE, "Socket listen: ", "\n");
244 return ERROR_CODE;
245 }
246 }
247
248 // bind the listenning socket
249 if (ERROR_OCCURRED(bind(listening_id, address, addrlen))) {
250 socket_print_error(stderr, ERROR_CODE, "Socket bind: ", "\n");
251 return ERROR_CODE;
252 }
253
254 if (verbose)
255 printf("Socket %d listenning at %d\n", listening_id, port);
256
257 socket_id = listening_id;
258
259 // do count times
260 // or indefinitely if set to a negative value
261 while (count) {
262
263 addrlen = max_length;
264 if (type == SOCK_STREAM) {
265 // acceept a socket if the stream socket is used
266 socket_id = accept(listening_id, address, &addrlen);
267 if (socket_id <= 0) {
268 socket_print_error(stderr, socket_id, "Socket accept: ", "\n");
269 } else {
270 if (verbose)
271 printf("Socket %d accepted\n", socket_id);
272 }
273 }
274
275 // if the datagram socket is used or the stream socked was accepted
276 if (socket_id > 0) {
277
278 // receive an echo request
279 value = recvfrom(socket_id, data, size, 0, address, &addrlen);
280 if (value < 0) {
281 socket_print_error(stderr, value, "Socket receive: ", "\n");
282 } else {
283 length = (size_t) value;
284 if (verbose) {
285 // print the header
286
287 // get the source port and prepare the address buffer
288 address_start = NULL;
289 switch (address->sa_family) {
290 case AF_INET:
291 port = ntohs(address_in->sin_port);
292 address_start = (uint8_t *) &address_in->sin_addr.s_addr;
293 break;
294 case AF_INET6:
295 port = ntohs(address_in6->sin6_port);
296 address_start = (uint8_t *) &address_in6->sin6_addr.s6_addr;
297 break;
298 default:
299 fprintf(stderr, "Address family %d (0x%X) is not supported.\n", address->sa_family);
300 }
301 // parse the source address
302 if (address_start) {
303 if (ERROR_OCCURRED(inet_ntop(address->sa_family, address_start, address_string, sizeof(address_string)))) {
304 fprintf(stderr, "Received address error %d\n", ERROR_CODE);
305 } else {
306 data[length] = '\0';
307 printf("Socket %d received %d bytes from %s:%d\n%s\n", socket_id, length, address_string, port, data);
308 }
309 }
310 }
311
312 // answer the request either with the static reply or the original data
313 if (ERROR_OCCURRED(sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen)))
314 socket_print_error(stderr, ERROR_CODE, "Socket send: ", "\n");
315 }
316
317 // close the accepted stream socket
318 if (type == SOCK_STREAM) {
319 if (ERROR_OCCURRED(closesocket(socket_id)))
320 socket_print_error(stderr, ERROR_CODE, "Close socket: ", "\n");
321 }
322
323 }
324
325 // decrease the count if positive
326 if (count > 0) {
327 count--;
328 if (verbose)
329 printf("Waiting for next %d packet(s)\n", count);
330 }
331 }
332
333 if (verbose)
334 printf("Closing the socket\n");
335
336 // close the listenning socket
337 if (ERROR_OCCURRED(closesocket(listening_id))) {
338 socket_print_error(stderr, ERROR_CODE, "Close socket: ", "\n");
339 return ERROR_CODE;
340 }
341
342 if (verbose)
343 printf("Exiting\n");
344
345 return EOK;
346}
347
348/** @}
349 */
Note: See TracBrowser for help on using the repository browser.