source: mainline/uspace/app/netecho/netecho.c@ 7e752b2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7e752b2 was 7e752b2, checked in by Martin Decky <martin@…>, 15 years ago
  • correct printf() formatting strings and corresponding arguments
  • minor cstyle changes and other small fixes
  • Property mode set to 100644
File size: 10.8 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>
[21580dd]43
[e4554d4]44#include <net/in.h>
45#include <net/in6.h>
46#include <net/inet.h>
[d9e2e0e]47#include <net/socket.h>
48#include <net/socket_parse.h>
[21580dd]49
[849ed54]50#include "print_error.h"
[21580dd]51
[a8e5051]52/** Network echo module name. */
[849ed54]53#define NAME "Network Echo"
[21580dd]54
[a8e5051]55static void echo_print_help(void)
56{
[21580dd]57 printf(
58 "Network Echo aplication\n" \
59 "Usage: echo [options]\n" \
60 "Where options are:\n" \
61 "-b backlog | --backlog=size\n" \
62 "\tThe size of the accepted sockets queue. Only for SOCK_STREAM. The default is 3.\n" \
63 "\n" \
64 "-c count | --count=count\n" \
65 "\tThe number of received messages to handle. A negative number means infinity. The default is infinity.\n" \
66 "\n" \
67 "-f protocol_family | --family=protocol_family\n" \
68 "\tThe listenning socket protocol family. Only the PF_INET and PF_INET6 are supported.\n"
69 "\n" \
70 "-h | --help\n" \
71 "\tShow this application help.\n"
72 "\n" \
73 "-p port_number | --port=port_number\n" \
74 "\tThe port number the application should listen at. The default is 7.\n" \
75 "\n" \
76 "-r reply_string | --reply=reply_string\n" \
77 "\tThe constant reply string. The default is the original data received.\n" \
78 "\n" \
79 "-s receive_size | --size=receive_size\n" \
80 "\tThe maximum receive data size the application should accept. The default is 1024 bytes.\n" \
81 "\n" \
82 "-t socket_type | --type=socket_type\n" \
83 "\tThe listenning socket type. Only the SOCK_DGRAM and the SOCK_STREAM are supported.\n" \
84 "\n" \
85 "-v | --verbose\n" \
86 "\tShow all output messages.\n"
87 );
88}
89
[a8e5051]90int main(int argc, char *argv[])
91{
92 size_t size = 1024;
93 int verbose = 0;
94 char *reply = NULL;
95 sock_type_t type = SOCK_DGRAM;
96 int count = -1;
97 int family = PF_INET;
98 uint16_t port = 7;
99 int backlog = 3;
[aadf01e]100
[a8e5051]101 socklen_t max_length = sizeof(struct sockaddr_in6);
[aadf01e]102 uint8_t address_data[max_length];
[a8e5051]103 struct sockaddr *address = (struct sockaddr *) address_data;
104 struct sockaddr_in *address_in = (struct sockaddr_in *) address;
105 struct sockaddr_in6 *address_in6 = (struct sockaddr_in6 *) address;
[aadf01e]106 socklen_t addrlen;
107 char address_string[INET6_ADDRSTRLEN];
[a8e5051]108 uint8_t *address_start;
[aadf01e]109 int socket_id;
110 int listening_id;
[a8e5051]111 char *data;
[aadf01e]112 size_t length;
113 int index;
114 size_t reply_length;
115 int value;
[5d0f1bc]116 int rc;
[aadf01e]117
[3be62bc]118 // parse the command line arguments
[a8e5051]119 for (index = 1; index < argc; ++ index) {
120 if (argv[index][0] == '-') {
121 switch (argv[index][1]) {
122 case 'b':
[5d0f1bc]123 rc = arg_parse_int(argc, argv, &index, &backlog, 0);
124 if (rc != EOK)
125 return rc;
[a8e5051]126 break;
127 case 'c':
[5d0f1bc]128 rc = arg_parse_int(argc, argv, &index, &count, 0);
129 if (rc != EOK)
130 return rc;
[a8e5051]131 break;
132 case 'f':
[5d0f1bc]133 rc = arg_parse_name_int(argc, argv, &index, &family, 0, socket_parse_protocol_family);
134 if (rc != EOK)
135 return rc;
[a8e5051]136 break;
137 case 'h':
138 echo_print_help();
139 return EOK;
140 break;
141 case 'p':
[5d0f1bc]142 rc = arg_parse_int(argc, argv, &index, &value, 0);
143 if (rc != EOK)
144 return rc;
[a8e5051]145 port = (uint16_t) value;
146 break;
147 case 'r':
[5d0f1bc]148 rc = arg_parse_string(argc, argv, &index, &reply, 0);
149 if (rc != EOK)
150 return rc;
[a8e5051]151 break;
152 case 's':
[5d0f1bc]153 rc = arg_parse_int(argc, argv, &index, &value, 0);
154 if (rc != EOK)
155 return rc;
[a8e5051]156 size = (value >= 0) ? (size_t) value : 0;
157 break;
158 case 't':
[5d0f1bc]159 rc = arg_parse_name_int(argc, argv, &index, &value, 0, socket_parse_socket_type);
160 if (rc != EOK)
161 return rc;
[a8e5051]162 type = (sock_type_t) value;
163 break;
164 case 'v':
165 verbose = 1;
166 break;
167 // long options with the double minus sign ('-')
168 case '-':
169 if (str_lcmp(argv[index] + 2, "backlog=", 6) == 0) {
[5d0f1bc]170 rc = arg_parse_int(argc, argv, &index, &backlog, 8);
171 if (rc != EOK)
172 return rc;
[a8e5051]173 } else if (str_lcmp(argv[index] + 2, "count=", 6) == 0) {
[5d0f1bc]174 rc = arg_parse_int(argc, argv, &index, &count, 8);
[481b212]175 if (rc != EOK)
176 return rc;
[a8e5051]177 } else if (str_lcmp(argv[index] + 2, "family=", 7) == 0) {
[5d0f1bc]178 rc = arg_parse_name_int(argc, argv, &index, &family, 9, socket_parse_protocol_family);
179 if (rc != EOK)
180 return rc;
[a8e5051]181 } else if (str_lcmp(argv[index] + 2, "help", 5) == 0) {
[aadf01e]182 echo_print_help();
183 return EOK;
[a8e5051]184 } else if (str_lcmp(argv[index] + 2, "port=", 5) == 0) {
[5d0f1bc]185 rc = arg_parse_int(argc, argv, &index, &value, 7);
186 if (rc != EOK)
187 return rc;
[aadf01e]188 port = (uint16_t) value;
[a8e5051]189 } else if (str_lcmp(argv[index] + 2, "reply=", 6) == 0) {
[5d0f1bc]190 rc = arg_parse_string(argc, argv, &index, &reply, 8);
191 if (rc != EOK)
192 return rc;
[a8e5051]193 } else if (str_lcmp(argv[index] + 2, "size=", 5) == 0) {
[5d0f1bc]194 rc = arg_parse_int(argc, argv, &index, &value, 7);
195 if (rc != EOK)
196 return rc;
[aadf01e]197 size = (value >= 0) ? (size_t) value : 0;
[a8e5051]198 } else if (str_lcmp(argv[index] + 2, "type=", 5) == 0) {
[5d0f1bc]199 rc = arg_parse_name_int(argc, argv, &index, &value, 7, socket_parse_socket_type);
200 if (rc != EOK)
201 return rc;
[aadf01e]202 type = (sock_type_t) value;
[a8e5051]203 } else if (str_lcmp(argv[index] + 2, "verbose", 8) == 0) {
[aadf01e]204 verbose = 1;
[a8e5051]205 } else {
[21580dd]206 echo_print_help();
207 return EINVAL;
[a8e5051]208 }
209 break;
210 default:
211 echo_print_help();
212 return EINVAL;
[21580dd]213 }
[a8e5051]214 } else {
[21580dd]215 echo_print_help();
216 return EINVAL;
217 }
218 }
219
[3be62bc]220 // check the buffer size
[a8e5051]221 if (size <= 0) {
[7e752b2]222 fprintf(stderr, "Receive size too small (%zu). Using 1024 bytes instead.\n", size);
[21580dd]223 size = 1024;
224 }
[3be62bc]225 // size plus the terminating null (\0)
[aadf01e]226 data = (char *) malloc(size + 1);
[a8e5051]227 if (!data) {
[aadf01e]228 fprintf(stderr, "Failed to allocate receive buffer.\n");
[21580dd]229 return ENOMEM;
230 }
231
[3be62bc]232 // set the reply size if set
[aadf01e]233 reply_length = reply ? str_length(reply) : 0;
[21580dd]234
[3be62bc]235 // prepare the address buffer
[aadf01e]236 bzero(address_data, max_length);
[a8e5051]237 switch (family) {
238 case PF_INET:
239 address_in->sin_family = AF_INET;
240 address_in->sin_port = htons(port);
241 addrlen = sizeof(struct sockaddr_in);
242 break;
243 case PF_INET6:
244 address_in6->sin6_family = AF_INET6;
245 address_in6->sin6_port = htons(port);
246 addrlen = sizeof(struct sockaddr_in6);
247 break;
248 default:
249 fprintf(stderr, "Protocol family is not supported\n");
250 return EAFNOSUPPORT;
[21580dd]251 }
252
[3be62bc]253 // get a listening socket
[aadf01e]254 listening_id = socket(family, type, 0);
[a8e5051]255 if (listening_id < 0) {
[aadf01e]256 socket_print_error(stderr, listening_id, "Socket create: ", "\n");
[21580dd]257 return listening_id;
258 }
259
[3be62bc]260 // if the stream socket is used
[a8e5051]261 if (type == SOCK_STREAM) {
[3be62bc]262 // check the backlog
[a8e5051]263 if (backlog <= 0) {
[7e752b2]264 fprintf(stderr, "Accepted sockets queue size too small (%zu). Using 3 instead.\n", size);
[21580dd]265 backlog = 3;
266 }
[3be62bc]267 // set the backlog
[5d0f1bc]268 rc = listen(listening_id, backlog);
269 if (rc != EOK) {
270 socket_print_error(stderr, rc, "Socket listen: ", "\n");
271 return rc;
[21580dd]272 }
273 }
274
[3be62bc]275 // bind the listenning socket
[5d0f1bc]276 rc = bind(listening_id, address, addrlen);
277 if (rc != EOK) {
278 socket_print_error(stderr, rc, "Socket bind: ", "\n");
279 return rc;
[21580dd]280 }
281
[a8e5051]282 if (verbose)
[aadf01e]283 printf("Socket %d listenning at %d\n", listening_id, port);
[2d68c72]284
285 socket_id = listening_id;
[21580dd]286
[3be62bc]287 // do count times
288 // or indefinitely if set to a negative value
[a8e5051]289 while (count) {
[3be62bc]290
[21580dd]291 addrlen = max_length;
[a8e5051]292 if (type == SOCK_STREAM) {
[3be62bc]293 // acceept a socket if the stream socket is used
[aadf01e]294 socket_id = accept(listening_id, address, &addrlen);
[a8e5051]295 if (socket_id <= 0) {
[aadf01e]296 socket_print_error(stderr, socket_id, "Socket accept: ", "\n");
[a8e5051]297 } else {
298 if (verbose)
[aadf01e]299 printf("Socket %d accepted\n", socket_id);
[21580dd]300 }
301 }
[3be62bc]302
303 // if the datagram socket is used or the stream socked was accepted
[a8e5051]304 if (socket_id > 0) {
[3be62bc]305
306 // receive an echo request
[aadf01e]307 value = recvfrom(socket_id, data, size, 0, address, &addrlen);
[a8e5051]308 if (value < 0) {
[aadf01e]309 socket_print_error(stderr, value, "Socket receive: ", "\n");
[a8e5051]310 } else {
[aadf01e]311 length = (size_t) value;
[a8e5051]312 if (verbose) {
[3be62bc]313 // print the header
314
315 // get the source port and prepare the address buffer
[21580dd]316 address_start = NULL;
[a8e5051]317 switch (address->sa_family) {
318 case AF_INET:
319 port = ntohs(address_in->sin_port);
320 address_start = (uint8_t *) &address_in->sin_addr.s_addr;
321 break;
322 case AF_INET6:
323 port = ntohs(address_in6->sin6_port);
324 address_start = (uint8_t *) &address_in6->sin6_addr.s6_addr;
325 break;
326 default:
[7e752b2]327 fprintf(stderr, "Address family %u (%#x) is not supported.\n",
328 address->sa_family, address->sa_family);
[21580dd]329 }
[3be62bc]330 // parse the source address
[a8e5051]331 if (address_start) {
[5d0f1bc]332 rc = inet_ntop(address->sa_family, address_start, address_string, sizeof(address_string));
333 if (rc != EOK) {
334 fprintf(stderr, "Received address error %d\n", rc);
[a8e5051]335 } else {
[aadf01e]336 data[length] = '\0';
[7e752b2]337 printf("Socket %d received %zu bytes from %s:%d\n%s\n",
338 socket_id, length, address_string, port, data);
[21580dd]339 }
340 }
341 }
[3be62bc]342
343 // answer the request either with the static reply or the original data
[5d0f1bc]344 rc = sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen);
345 if (rc != EOK)
346 socket_print_error(stderr, rc, "Socket send: ", "\n");
[21580dd]347 }
[3be62bc]348
349 // close the accepted stream socket
[a8e5051]350 if (type == SOCK_STREAM) {
[5d0f1bc]351 rc = closesocket(socket_id);
352 if (rc != EOK)
353 socket_print_error(stderr, rc, "Close socket: ", "\n");
[21580dd]354 }
[3be62bc]355
[21580dd]356 }
[3be62bc]357
358 // decrease the count if positive
[a8e5051]359 if (count > 0) {
360 count--;
361 if (verbose)
[aadf01e]362 printf("Waiting for next %d packet(s)\n", count);
[21580dd]363 }
364 }
365
[a8e5051]366 if (verbose)
[aadf01e]367 printf("Closing the socket\n");
[21580dd]368
[3be62bc]369 // close the listenning socket
[5d0f1bc]370 rc = closesocket(listening_id);
371 if (rc != EOK) {
372 socket_print_error(stderr, rc, "Close socket: ", "\n");
373 return rc;
[21580dd]374 }
375
[a8e5051]376 if (verbose)
[aadf01e]377 printf("Exiting\n");
[21580dd]378
379 return EOK;
380}
381
382/** @}
383 */
Note: See TracBrowser for help on using the repository browser.