source: mainline/uspace/app/netecho/netecho.c@ c442f63

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c442f63 was c442f63, checked in by Martin Decky <martin@…>, 12 years ago

nettest fixes (thx Antonin Steinhauser)

  • Property mode set to 100644
File size: 12.1 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
[631ee0c]34 * Network echo server.
35 *
36 * Sockets-based server that echoes incomming messages. If stream mode
37 * is selected, accepts incoming connections.
[21580dd]38 */
39
[f57aebc]40#include <assert.h>
[21580dd]41#include <stdio.h>
[c5ad226]42#include <stdlib.h>
[19f857a]43#include <str.h>
[21580dd]44#include <task.h>
[2721a75]45#include <arg_parse.h>
[21580dd]46
[e4554d4]47#include <net/in.h>
48#include <net/in6.h>
49#include <net/inet.h>
[d9e2e0e]50#include <net/socket.h>
51#include <net/socket_parse.h>
[21580dd]52
[849ed54]53#include "print_error.h"
[21580dd]54
[631ee0c]55#define NAME "netecho"
[21580dd]56
[c5ad226]57static int count = -1;
58static int family = PF_INET;
59static sock_type_t type = SOCK_DGRAM;
60static uint16_t port = 7;
61static int backlog = 3;
62static size_t size = 1024;
63static int verbose = 0;
64
65static char *reply = NULL;
[bfe6366]66static size_t reply_length;
67
68static char *data;
[c5ad226]69
[a8e5051]70static void echo_print_help(void)
71{
[21580dd]72 printf(
[af4f86f]73 "Network echo server\n"
74 "Usage: " NAME " [options]\n"
75 "Where options are:\n"
76 "-b backlog | --backlog=size\n"
77 "\tThe size of the accepted sockets queue. Only for SOCK_STREAM. "
78 "The default is 3.\n"
79 "\n"
80 "-c count | --count=count\n"
81 "\tThe number of received messages to handle. A negative number "
82 "means infinity. The default is infinity.\n"
83 "\n"
84 "-f protocol_family | --family=protocol_family\n"
85 "\tThe listenning socket protocol family. Only the PF_INET and "
86 "PF_INET6 are supported.\n"
87 "\n"
88 "-h | --help\n"
89 "\tShow this application help.\n"
90 "\n"
91 "-p port_number | --port=port_number\n"
92 "\tThe port number the application should listen at. The default "
93 "is 7.\n"
94 "\n"
95 "-r reply_string | --reply=reply_string\n"
96 "\tThe constant reply string. The default is the original data "
97 "received.\n"
98 "\n"
99 "-s receive_size | --size=receive_size\n"
100 "\tThe maximum receive data size the application should accept. "
101 "The default is 1024 bytes.\n"
102 "\n"
103 "-t socket_type | --type=socket_type\n"
104 "\tThe listenning socket type. Only the SOCK_DGRAM and the "
105 "SOCK_STREAM are supported.\n"
106 "\n"
107 "-v | --verbose\n"
108 "\tShow all output messages.\n"
[21580dd]109 );
110}
111
[c5ad226]112static int netecho_parse_option(int argc, char *argv[], int *index)
[a8e5051]113{
[c5ad226]114 int value;
115 int rc;
116
117 switch (argv[*index][1]) {
118 case 'b':
119 rc = arg_parse_int(argc, argv, index, &backlog, 0);
120 if (rc != EOK)
121 return rc;
122 break;
123 case 'c':
124 rc = arg_parse_int(argc, argv, index, &count, 0);
125 if (rc != EOK)
126 return rc;
127 break;
128 case 'f':
129 rc = arg_parse_name_int(argc, argv, index, &family, 0,
130 socket_parse_protocol_family);
131 if (rc != EOK)
132 return rc;
133 break;
134 case 'h':
135 echo_print_help();
136 exit(0);
137 break;
138 case 'p':
139 rc = arg_parse_int(argc, argv, index, &value, 0);
140 if (rc != EOK)
141 return rc;
142 port = (uint16_t) value;
143 break;
144 case 'r':
145 rc = arg_parse_string(argc, argv, index, &reply, 0);
146 if (rc != EOK)
147 return rc;
148 break;
149 case 's':
150 rc = arg_parse_int(argc, argv, index, &value, 0);
151 if (rc != EOK)
152 return rc;
153 size = (value >= 0) ? (size_t) value : 0;
154 break;
155 case 't':
156 rc = arg_parse_name_int(argc, argv, index, &value, 0,
157 socket_parse_socket_type);
158 if (rc != EOK)
159 return rc;
160 type = (sock_type_t) value;
161 break;
162 case 'v':
163 verbose = 1;
164 break;
165 /* Long options with double dash */
166 case '-':
167 if (str_lcmp(argv[*index] + 2, "backlog=", 6) == 0) {
168 rc = arg_parse_int(argc, argv, index, &backlog, 8);
169 if (rc != EOK)
170 return rc;
171 } else if (str_lcmp(argv[*index] + 2, "count=", 6) == 0) {
172 rc = arg_parse_int(argc, argv, index, &count, 8);
173 if (rc != EOK)
174 return rc;
175 } else if (str_lcmp(argv[*index] + 2, "family=", 7) == 0) {
176 rc = arg_parse_name_int(argc, argv, index, &family, 9,
177 socket_parse_protocol_family);
178 if (rc != EOK)
179 return rc;
180 } else if (str_lcmp(argv[*index] + 2, "help", 5) == 0) {
181 echo_print_help();
[cd22764]182 exit(0);
[c5ad226]183 } else if (str_lcmp(argv[*index] + 2, "port=", 5) == 0) {
184 rc = arg_parse_int(argc, argv, index, &value, 7);
185 if (rc != EOK)
186 return rc;
187 port = (uint16_t) value;
188 } else if (str_lcmp(argv[*index] + 2, "reply=", 6) == 0) {
189 rc = arg_parse_string(argc, argv, index, &reply, 8);
190 if (rc != EOK)
191 return rc;
192 } else if (str_lcmp(argv[*index] + 2, "size=", 5) == 0) {
193 rc = arg_parse_int(argc, argv, index, &value, 7);
194 if (rc != EOK)
195 return rc;
196 size = (value >= 0) ? (size_t) value : 0;
197 } else if (str_lcmp(argv[*index] + 2, "type=", 5) == 0) {
198 rc = arg_parse_name_int(argc, argv, index, &value, 7,
199 socket_parse_socket_type);
200 if (rc != EOK)
201 return rc;
202 type = (sock_type_t) value;
203 } else if (str_lcmp(argv[*index] + 2, "verbose", 8) == 0) {
204 verbose = 1;
205 } else {
206 echo_print_help();
207 return EINVAL;
208 }
209 break;
210 default:
211 echo_print_help();
212 return EINVAL;
213 }
214
215 return EOK;
216}
[aadf01e]217
[bfe6366]218/** Echo one message (accept one connection and echo message).
219 *
220 * @param listening_id Listening socket.
221 * @return EOK on success or negative error code.
222 */
[f57aebc]223static int netecho_socket_process_message(int listening_id)
[bfe6366]224{
[f57aebc]225 uint8_t address_buf[sizeof(struct sockaddr_in6)];
226
[c442f63]227 socklen_t addrlen = sizeof(struct sockaddr_in6);
[bfe6366]228 int socket_id;
229 ssize_t rcv_size;
230 size_t length;
231 uint8_t *address_start;
[f57aebc]232
[bfe6366]233 char address_string[INET6_ADDRSTRLEN];
[f57aebc]234 struct sockaddr_in *address_in = (struct sockaddr_in *) address_buf;
235 struct sockaddr_in6 *address_in6 = (struct sockaddr_in6 *) address_buf;
236 struct sockaddr *address = (struct sockaddr *) address_buf;
237
[bfe6366]238 int rc;
239
240 if (type == SOCK_STREAM) {
[f57aebc]241 /* Accept a socket if a stream socket is used */
[26ec91c]242 if (verbose)
243 printf("accept()\n");
[c85eb02]244 socket_id = accept(listening_id, (void *) address_buf, &addrlen);
[bfe6366]245 if (socket_id <= 0) {
246 socket_print_error(stderr, socket_id, "Socket accept: ", "\n");
247 } else {
248 if (verbose)
249 printf("Socket %d accepted\n", socket_id);
250 }
[f57aebc]251
252 assert((size_t) addrlen <= sizeof(address_buf));
[bfe6366]253 } else {
254 socket_id = listening_id;
255 }
256
257 /* if the datagram socket is used or the stream socked was accepted */
258 if (socket_id > 0) {
259
260 /* Receive a message to echo */
[26ec91c]261 if (verbose)
262 printf("recvfrom()\n");
[f57aebc]263 rcv_size = recvfrom(socket_id, data, size, 0, address,
264 &addrlen);
[bfe6366]265 if (rcv_size < 0) {
266 socket_print_error(stderr, rcv_size, "Socket receive: ", "\n");
267 } else {
268 length = (size_t) rcv_size;
269 if (verbose) {
270 /* Print the header */
271
272 /* Get the source port and prepare the address buffer */
273 address_start = NULL;
274 switch (address->sa_family) {
275 case AF_INET:
276 port = ntohs(address_in->sin_port);
277 address_start = (uint8_t *) &address_in->sin_addr.s_addr;
278 break;
279 case AF_INET6:
280 port = ntohs(address_in6->sin6_port);
[c442f63]281 address_start = (uint8_t *) address_in6->sin6_addr.s6_addr;
[bfe6366]282 break;
283 default:
284 fprintf(stderr, "Address family %u (%#x) is not supported.\n",
285 address->sa_family, address->sa_family);
286 }
287
288 /* Parse source address */
289 if (address_start) {
290 rc = inet_ntop(address->sa_family, address_start, address_string, sizeof(address_string));
291 if (rc != EOK) {
292 fprintf(stderr, "Received address error %d\n", rc);
293 } else {
294 data[length] = '\0';
295 printf("Socket %d received %zu bytes from %s:%d\n%s\n",
296 socket_id, length, address_string, port, data);
297 }
298 }
299 }
300
301 /* Answer the request either with the static reply or the original data */
[92e717c]302 if (type == SOCK_STREAM) {
[26ec91c]303 if (verbose)
304 printf("send()\n");
[92e717c]305 rc = send(socket_id, reply ? reply : data, reply ? reply_length : length, 0);
306 if (rc != EOK)
307 socket_print_error(stderr, rc, "Socket send: ", "\n");
308 } else {
[26ec91c]309 if (verbose)
310 printf("sendto()\n");
[92e717c]311 rc = sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen);
312 if (rc != EOK)
[c85eb02]313 socket_print_error(stderr, rc, "Socket sendto: ", "\n");
[92e717c]314 }
[bfe6366]315 }
316
317 /* Close accepted stream socket */
318 if (type == SOCK_STREAM) {
319 rc = closesocket(socket_id);
320 if (rc != EOK)
321 socket_print_error(stderr, rc, "Close socket: ", "\n");
322 }
323
324 }
325
326 return EOK;
327}
328
329
[c5ad226]330int main(int argc, char *argv[])
331{
[f57aebc]332 struct sockaddr *address;;
333 struct sockaddr_in address_in;
334 struct sockaddr_in6 address_in6;
[aadf01e]335 socklen_t addrlen;
[f57aebc]336
[aadf01e]337 int listening_id;
338 int index;
[5d0f1bc]339 int rc;
[aadf01e]340
[0349a10]341 /* Parse command line arguments */
[af4f86f]342 for (index = 1; index < argc; ++index) {
[a8e5051]343 if (argv[index][0] == '-') {
[c5ad226]344 rc = netecho_parse_option(argc, argv, &index);
345 if (rc != EOK)
346 return rc;
[a8e5051]347 } else {
[21580dd]348 echo_print_help();
349 return EINVAL;
350 }
351 }
352
[0349a10]353 /* Check buffer size */
[a8e5051]354 if (size <= 0) {
[7e752b2]355 fprintf(stderr, "Receive size too small (%zu). Using 1024 bytes instead.\n", size);
[21580dd]356 size = 1024;
357 }
[0349a10]358
359 /* size plus the terminating null character. */
[aadf01e]360 data = (char *) malloc(size + 1);
[a8e5051]361 if (!data) {
[aadf01e]362 fprintf(stderr, "Failed to allocate receive buffer.\n");
[21580dd]363 return ENOMEM;
364 }
365
[0349a10]366 /* Set the reply size if set */
[aadf01e]367 reply_length = reply ? str_length(reply) : 0;
[21580dd]368
[0349a10]369 /* Prepare the address buffer */
[a8e5051]370 switch (family) {
371 case PF_INET:
[f57aebc]372 address_in.sin_family = AF_INET;
373 address_in.sin_port = htons(port);
[92b42442]374 address_in.sin_addr.s_addr = INADDR_ANY;
[f57aebc]375 address = (struct sockaddr *) &address_in;
376 addrlen = sizeof(address_in);
[a8e5051]377 break;
378 case PF_INET6:
[f57aebc]379 address_in6.sin6_family = AF_INET6;
380 address_in6.sin6_port = htons(port);
[e52b4b5]381 address_in6.sin6_addr = in6addr_any;
[f57aebc]382 address = (struct sockaddr *) &address_in6;
383 addrlen = sizeof(address_in6);
[a8e5051]384 break;
385 default:
386 fprintf(stderr, "Protocol family is not supported\n");
387 return EAFNOSUPPORT;
[21580dd]388 }
389
[0349a10]390 /* Get a listening socket */
[aadf01e]391 listening_id = socket(family, type, 0);
[a8e5051]392 if (listening_id < 0) {
[aadf01e]393 socket_print_error(stderr, listening_id, "Socket create: ", "\n");
[21580dd]394 return listening_id;
395 }
[9164c0c]396
397 /* Bind the listening socket */
398 rc = bind(listening_id, address, addrlen);
399 if (rc != EOK) {
400 socket_print_error(stderr, rc, "Socket bind: ", "\n");
401 return rc;
402 }
403
[0349a10]404 /* if the stream socket is used */
[a8e5051]405 if (type == SOCK_STREAM) {
[0349a10]406 /* Check backlog size */
[a8e5051]407 if (backlog <= 0) {
[7e752b2]408 fprintf(stderr, "Accepted sockets queue size too small (%zu). Using 3 instead.\n", size);
[21580dd]409 backlog = 3;
410 }
[9164c0c]411
[0349a10]412 /* Set the backlog */
[5d0f1bc]413 rc = listen(listening_id, backlog);
414 if (rc != EOK) {
415 socket_print_error(stderr, rc, "Socket listen: ", "\n");
416 return rc;
[21580dd]417 }
418 }
419
[a8e5051]420 if (verbose)
[aadf01e]421 printf("Socket %d listenning at %d\n", listening_id, port);
[2d68c72]422
[0349a10]423 /*
424 * do count times
425 * or indefinitely if set to a negative value
426 */
[a8e5051]427 while (count) {
[f57aebc]428 rc = netecho_socket_process_message(listening_id);
[bfe6366]429 if (rc != EOK)
430 break;
[3be62bc]431
[0349a10]432 /* Decrease count if positive */
[a8e5051]433 if (count > 0) {
434 count--;
435 if (verbose)
[f57aebc]436 printf("Waiting for next %d message(s)\n", count);
[21580dd]437 }
438 }
439
[a8e5051]440 if (verbose)
[aadf01e]441 printf("Closing the socket\n");
[21580dd]442
[0349a10]443 /* Close listenning socket */
[5d0f1bc]444 rc = closesocket(listening_id);
445 if (rc != EOK) {
446 socket_print_error(stderr, rc, "Close socket: ", "\n");
447 return rc;
[21580dd]448 }
449
[a8e5051]450 if (verbose)
[aadf01e]451 printf("Exiting\n");
[21580dd]452
453 return EOK;
454}
455
456/** @}
457 */
Note: See TracBrowser for help on using the repository browser.