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
RevLine 
[21580dd]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
[849ed54]29/** @addtogroup netecho
[a8e5051]30 * @{
[21580dd]31 */
32
33/** @file
[a8e5051]34 * Network echo application.
35 * Answers received packets.
[21580dd]36 */
37
38#include <malloc.h>
39#include <stdio.h>
[19f857a]40#include <str.h>
[21580dd]41#include <task.h>
[2721a75]42#include <arg_parse.h>
[c5b59ce]43#include <err.h>
[21580dd]44
[e4554d4]45#include <net/in.h>
46#include <net/in6.h>
47#include <net/inet.h>
[d9e2e0e]48#include <net/socket.h>
49#include <net/socket_parse.h>
[21580dd]50
[849ed54]51#include "print_error.h"
[21580dd]52
[a8e5051]53/** Network echo module name. */
[849ed54]54#define NAME "Network Echo"
[21580dd]55
[a8e5051]56static void echo_print_help(void)
57{
[21580dd]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
[a8e5051]91int main(int argc, char *argv[])
92{
[21580dd]93 ERROR_DECLARE;
94
[a8e5051]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;
[aadf01e]103
[a8e5051]104 socklen_t max_length = sizeof(struct sockaddr_in6);
[aadf01e]105 uint8_t address_data[max_length];
[a8e5051]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;
[aadf01e]109 socklen_t addrlen;
110 char address_string[INET6_ADDRSTRLEN];
[a8e5051]111 uint8_t *address_start;
[aadf01e]112 int socket_id;
113 int listening_id;
[a8e5051]114 char *data;
[aadf01e]115 size_t length;
116 int index;
117 size_t reply_length;
118 int value;
119
[3be62bc]120 // parse the command line arguments
[a8e5051]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) {
[aadf01e]164 echo_print_help();
165 return EOK;
[a8e5051]166 } else if (str_lcmp(argv[index] + 2, "port=", 5) == 0) {
167 ERROR_PROPAGATE(arg_parse_int(argc, argv, &index, &value, 7));
[aadf01e]168 port = (uint16_t) value;
[a8e5051]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));
[aadf01e]173 size = (value >= 0) ? (size_t) value : 0;
[a8e5051]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));
[aadf01e]176 type = (sock_type_t) value;
[a8e5051]177 } else if (str_lcmp(argv[index] + 2, "verbose", 8) == 0) {
[aadf01e]178 verbose = 1;
[a8e5051]179 } else {
[21580dd]180 echo_print_help();
181 return EINVAL;
[a8e5051]182 }
183 break;
184 default:
185 echo_print_help();
186 return EINVAL;
[21580dd]187 }
[a8e5051]188 } else {
[21580dd]189 echo_print_help();
190 return EINVAL;
191 }
192 }
193
[3be62bc]194 // check the buffer size
[a8e5051]195 if (size <= 0) {
[aadf01e]196 fprintf(stderr, "Receive size too small (%d). Using 1024 bytes instead.\n", size);
[21580dd]197 size = 1024;
198 }
[3be62bc]199 // size plus the terminating null (\0)
[aadf01e]200 data = (char *) malloc(size + 1);
[a8e5051]201 if (!data) {
[aadf01e]202 fprintf(stderr, "Failed to allocate receive buffer.\n");
[21580dd]203 return ENOMEM;
204 }
205
[3be62bc]206 // set the reply size if set
[aadf01e]207 reply_length = reply ? str_length(reply) : 0;
[21580dd]208
[3be62bc]209 // prepare the address buffer
[aadf01e]210 bzero(address_data, max_length);
[a8e5051]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;
[21580dd]225 }
226
[3be62bc]227 // get a listening socket
[aadf01e]228 listening_id = socket(family, type, 0);
[a8e5051]229 if (listening_id < 0) {
[aadf01e]230 socket_print_error(stderr, listening_id, "Socket create: ", "\n");
[21580dd]231 return listening_id;
232 }
233
[3be62bc]234 // if the stream socket is used
[a8e5051]235 if (type == SOCK_STREAM) {
[3be62bc]236 // check the backlog
[a8e5051]237 if (backlog <= 0) {
[aadf01e]238 fprintf(stderr, "Accepted sockets queue size too small (%d). Using 3 instead.\n", size);
[21580dd]239 backlog = 3;
240 }
[3be62bc]241 // set the backlog
[a8e5051]242 if (ERROR_OCCURRED(listen(listening_id, backlog))) {
[aadf01e]243 socket_print_error(stderr, ERROR_CODE, "Socket listen: ", "\n");
[21580dd]244 return ERROR_CODE;
245 }
246 }
247
[3be62bc]248 // bind the listenning socket
[a8e5051]249 if (ERROR_OCCURRED(bind(listening_id, address, addrlen))) {
[aadf01e]250 socket_print_error(stderr, ERROR_CODE, "Socket bind: ", "\n");
[21580dd]251 return ERROR_CODE;
252 }
253
[a8e5051]254 if (verbose)
[aadf01e]255 printf("Socket %d listenning at %d\n", listening_id, port);
[2d68c72]256
257 socket_id = listening_id;
[21580dd]258
[3be62bc]259 // do count times
260 // or indefinitely if set to a negative value
[a8e5051]261 while (count) {
[3be62bc]262
[21580dd]263 addrlen = max_length;
[a8e5051]264 if (type == SOCK_STREAM) {
[3be62bc]265 // acceept a socket if the stream socket is used
[aadf01e]266 socket_id = accept(listening_id, address, &addrlen);
[a8e5051]267 if (socket_id <= 0) {
[aadf01e]268 socket_print_error(stderr, socket_id, "Socket accept: ", "\n");
[a8e5051]269 } else {
270 if (verbose)
[aadf01e]271 printf("Socket %d accepted\n", socket_id);
[21580dd]272 }
273 }
[3be62bc]274
275 // if the datagram socket is used or the stream socked was accepted
[a8e5051]276 if (socket_id > 0) {
[3be62bc]277
278 // receive an echo request
[aadf01e]279 value = recvfrom(socket_id, data, size, 0, address, &addrlen);
[a8e5051]280 if (value < 0) {
[aadf01e]281 socket_print_error(stderr, value, "Socket receive: ", "\n");
[a8e5051]282 } else {
[aadf01e]283 length = (size_t) value;
[a8e5051]284 if (verbose) {
[3be62bc]285 // print the header
286
287 // get the source port and prepare the address buffer
[21580dd]288 address_start = NULL;
[a8e5051]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);
[21580dd]300 }
[3be62bc]301 // parse the source address
[a8e5051]302 if (address_start) {
303 if (ERROR_OCCURRED(inet_ntop(address->sa_family, address_start, address_string, sizeof(address_string)))) {
[aadf01e]304 fprintf(stderr, "Received address error %d\n", ERROR_CODE);
[a8e5051]305 } else {
[aadf01e]306 data[length] = '\0';
307 printf("Socket %d received %d bytes from %s:%d\n%s\n", socket_id, length, address_string, port, data);
[21580dd]308 }
309 }
310 }
[3be62bc]311
312 // answer the request either with the static reply or the original data
[a8e5051]313 if (ERROR_OCCURRED(sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen)))
[aadf01e]314 socket_print_error(stderr, ERROR_CODE, "Socket send: ", "\n");
[21580dd]315 }
[3be62bc]316
317 // close the accepted stream socket
[a8e5051]318 if (type == SOCK_STREAM) {
319 if (ERROR_OCCURRED(closesocket(socket_id)))
[aadf01e]320 socket_print_error(stderr, ERROR_CODE, "Close socket: ", "\n");
[21580dd]321 }
[3be62bc]322
[21580dd]323 }
[3be62bc]324
325 // decrease the count if positive
[a8e5051]326 if (count > 0) {
327 count--;
328 if (verbose)
[aadf01e]329 printf("Waiting for next %d packet(s)\n", count);
[21580dd]330 }
331 }
332
[a8e5051]333 if (verbose)
[aadf01e]334 printf("Closing the socket\n");
[21580dd]335
[3be62bc]336 // close the listenning socket
[a8e5051]337 if (ERROR_OCCURRED(closesocket(listening_id))) {
[aadf01e]338 socket_print_error(stderr, ERROR_CODE, "Close socket: ", "\n");
[21580dd]339 return ERROR_CODE;
340 }
341
[a8e5051]342 if (verbose)
[aadf01e]343 printf("Exiting\n");
[21580dd]344
345 return EOK;
346}
347
348/** @}
349 */
Note: See TracBrowser for help on using the repository browser.