netecho.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009 Lukas Mejdrech
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00040 #include <assert.h>
00041 #include <stdio.h>
00042 #include <stdlib.h>
00043 #include <str.h>
00044 #include <task.h>
00045 #include <arg_parse.h>
00046 
00047 #include <net/in.h>
00048 #include <net/in6.h>
00049 #include <net/inet.h>
00050 #include <net/socket.h>
00051 #include <net/socket_parse.h>
00052 
00053 #include "print_error.h"
00054 
00055 #define NAME "netecho"
00056 
00057 static int count = -1;
00058 static int family = PF_INET;
00059 static sock_type_t type = SOCK_DGRAM;
00060 static uint16_t port = 7;
00061 static int backlog = 3;
00062 static size_t size = 1024;
00063 static int verbose = 0;
00064 
00065 static char *reply = NULL;
00066 static size_t reply_length;
00067 
00068 static char *data;
00069 
00070 static void echo_print_help(void)
00071 {
00072         printf(
00073             "Network echo server\n"
00074             "Usage: " NAME " [options]\n"
00075             "Where options are:\n"
00076             "-b backlog | --backlog=size\n"
00077             "\tThe size of the accepted sockets queue. Only for SOCK_STREAM. "
00078             "The default is 3.\n"
00079             "\n"
00080             "-c count | --count=count\n"
00081             "\tThe number of received messages to handle. A negative number "
00082             "means infinity. The default is infinity.\n"
00083             "\n"
00084             "-f protocol_family | --family=protocol_family\n"
00085             "\tThe listenning socket protocol family. Only the PF_INET and "
00086             "PF_INET6 are supported.\n"
00087             "\n"
00088             "-h | --help\n"
00089             "\tShow this application help.\n"
00090             "\n"
00091             "-p port_number | --port=port_number\n"
00092             "\tThe port number the application should listen at. The default "
00093             "is 7.\n"
00094             "\n"
00095             "-r reply_string | --reply=reply_string\n"
00096             "\tThe constant reply string. The default is the original data "
00097             "received.\n"
00098             "\n"
00099             "-s receive_size | --size=receive_size\n"
00100             "\tThe maximum receive data size the application should accept. "
00101             "The default is 1024 bytes.\n"
00102             "\n"
00103             "-t socket_type | --type=socket_type\n"
00104             "\tThe listenning socket type. Only the SOCK_DGRAM and the "
00105             "SOCK_STREAM are supported.\n"
00106             "\n"
00107             "-v | --verbose\n"
00108             "\tShow all output messages.\n"
00109         );
00110 }
00111 
00112 static int netecho_parse_option(int argc, char *argv[], int *index)
00113 {
00114         int value;
00115         int rc;
00116 
00117         switch (argv[*index][1]) {
00118         case 'b':
00119                 rc = arg_parse_int(argc, argv, index, &backlog, 0);
00120                 if (rc != EOK)
00121                         return rc;
00122                 break;
00123         case 'c':
00124                 rc = arg_parse_int(argc, argv, index, &count, 0);
00125                 if (rc != EOK)
00126                         return rc;
00127                 break;
00128         case 'f':
00129                 rc = arg_parse_name_int(argc, argv, index, &family, 0,
00130                     socket_parse_protocol_family);
00131                 if (rc != EOK)
00132                         return rc;
00133                 break;
00134         case 'h':
00135                 echo_print_help();
00136                 exit(0);
00137                 break;
00138         case 'p':
00139                 rc = arg_parse_int(argc, argv, index, &value, 0);
00140                 if (rc != EOK)
00141                         return rc;
00142                 port = (uint16_t) value;
00143                 break;
00144         case 'r':
00145                 rc = arg_parse_string(argc, argv, index, &reply, 0);
00146                 if (rc != EOK)
00147                         return rc;
00148                 break;
00149         case 's':
00150                 rc = arg_parse_int(argc, argv, index, &value, 0);
00151                 if (rc != EOK)
00152                         return rc;
00153                 size = (value >= 0) ? (size_t) value : 0;
00154                 break;
00155         case 't':
00156                 rc = arg_parse_name_int(argc, argv, index, &value, 0,
00157                     socket_parse_socket_type);
00158                 if (rc != EOK)
00159                         return rc;
00160                 type = (sock_type_t) value;
00161                 break;
00162         case 'v':
00163                 verbose = 1;
00164                 break;
00165         /* Long options with double dash */
00166         case '-':
00167                 if (str_lcmp(argv[*index] + 2, "backlog=", 6) == 0) {
00168                         rc = arg_parse_int(argc, argv, index, &backlog, 8);
00169                         if (rc != EOK)
00170                                 return rc;
00171                 } else if (str_lcmp(argv[*index] + 2, "count=", 6) == 0) {
00172                         rc = arg_parse_int(argc, argv, index, &count, 8);
00173                         if (rc != EOK)
00174                                 return rc;
00175                 } else if (str_lcmp(argv[*index] + 2, "family=", 7) == 0) {
00176                         rc = arg_parse_name_int(argc, argv, index, &family, 9,
00177                             socket_parse_protocol_family);
00178                         if (rc != EOK)
00179                                 return rc;
00180                 } else if (str_lcmp(argv[*index] + 2, "help", 5) == 0) {
00181                         echo_print_help();
00182                         exit(0);
00183                 } else if (str_lcmp(argv[*index] + 2, "port=", 5) == 0) {
00184                         rc = arg_parse_int(argc, argv, index, &value, 7);
00185                         if (rc != EOK)
00186                                 return rc;
00187                         port = (uint16_t) value;
00188                 } else if (str_lcmp(argv[*index] + 2, "reply=", 6) == 0) {
00189                         rc = arg_parse_string(argc, argv, index, &reply, 8);
00190                         if (rc != EOK)
00191                                 return rc;
00192                 } else if (str_lcmp(argv[*index] + 2, "size=", 5) == 0) {
00193                         rc = arg_parse_int(argc, argv, index, &value, 7);
00194                         if (rc != EOK)
00195                                 return rc;
00196                         size = (value >= 0) ? (size_t) value : 0;
00197                 } else if (str_lcmp(argv[*index] + 2, "type=", 5) == 0) {
00198                         rc = arg_parse_name_int(argc, argv, index, &value, 7,
00199                             socket_parse_socket_type);
00200                         if (rc != EOK)
00201                                 return rc;
00202                         type = (sock_type_t) value;
00203                 } else if (str_lcmp(argv[*index] + 2, "verbose", 8) == 0) {
00204                         verbose = 1;
00205                 } else {
00206                         echo_print_help();
00207                         return EINVAL;
00208                 }
00209                 break;
00210         default:
00211                 echo_print_help();
00212                 return EINVAL;
00213         }
00214 
00215         return EOK;
00216 }
00217 
00223 static int netecho_socket_process_message(int listening_id)
00224 {
00225         uint8_t address_buf[sizeof(struct sockaddr_in6)];
00226 
00227         socklen_t addrlen;
00228         int socket_id;
00229         ssize_t rcv_size;
00230         size_t length;
00231         uint8_t *address_start;
00232 
00233         char address_string[INET6_ADDRSTRLEN];
00234         struct sockaddr_in *address_in = (struct sockaddr_in *) address_buf;
00235         struct sockaddr_in6 *address_in6 = (struct sockaddr_in6 *) address_buf;
00236         struct sockaddr *address = (struct sockaddr *) address_buf;
00237 
00238         int rc;
00239 
00240         if (type == SOCK_STREAM) {
00241                 /* Accept a socket if a stream socket is used */
00242                 addrlen = sizeof(address_buf);
00243                 socket_id = accept(listening_id, (void *) address_buf, &addrlen);
00244                 if (socket_id <= 0) {
00245                         socket_print_error(stderr, socket_id, "Socket accept: ", "\n");
00246                 } else {
00247                         if (verbose)
00248                                 printf("Socket %d accepted\n", socket_id);
00249                 }
00250 
00251                 assert((size_t) addrlen <= sizeof(address_buf));
00252         } else {
00253                 socket_id = listening_id;
00254         }
00255 
00256         /* if the datagram socket is used or the stream socked was accepted */
00257         if (socket_id > 0) {
00258 
00259                 /* Receive a message to echo */
00260                 rcv_size = recvfrom(socket_id, data, size, 0, address,
00261                     &addrlen);
00262                 if (rcv_size < 0) {
00263                         socket_print_error(stderr, rcv_size, "Socket receive: ", "\n");
00264                 } else {
00265                         length = (size_t) rcv_size;
00266                         if (verbose) {
00267                                 /* Print the header */
00268 
00269                                 /* Get the source port and prepare the address buffer */
00270                                 address_start = NULL;
00271                                 switch (address->sa_family) {
00272                                 case AF_INET:
00273                                         port = ntohs(address_in->sin_port);
00274                                         address_start = (uint8_t *) &address_in->sin_addr.s_addr;
00275                                         break;
00276                                 case AF_INET6:
00277                                         port = ntohs(address_in6->sin6_port);
00278                                         address_start = (uint8_t *) &address_in6->sin6_addr.s6_addr;
00279                                         break;
00280                                 default:
00281                                         fprintf(stderr, "Address family %u (%#x) is not supported.\n",
00282                                             address->sa_family, address->sa_family);
00283                                 }
00284 
00285                                 /* Parse source address */
00286                                 if (address_start) {
00287                                         rc = inet_ntop(address->sa_family, address_start, address_string, sizeof(address_string));
00288                                         if (rc != EOK) {
00289                                                 fprintf(stderr, "Received address error %d\n", rc);
00290                                         } else {
00291                                                 data[length] = '\0';
00292                                                 printf("Socket %d received %zu bytes from %s:%d\n%s\n",
00293                                                     socket_id, length, address_string, port, data);
00294                                         }
00295                                 }
00296                         }
00297 
00298                         /* Answer the request either with the static reply or the original data */
00299                         rc = sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen);
00300                         if (rc != EOK)
00301                                 socket_print_error(stderr, rc, "Socket send: ", "\n");
00302                 }
00303 
00304                 /* Close accepted stream socket */
00305                 if (type == SOCK_STREAM) {
00306                         rc = closesocket(socket_id);
00307                         if (rc != EOK)
00308                                 socket_print_error(stderr, rc, "Close socket: ", "\n");
00309                 }
00310 
00311         }
00312 
00313         return EOK;
00314 }
00315 
00316 
00317 int main(int argc, char *argv[])
00318 {
00319         struct sockaddr *address;;
00320         struct sockaddr_in address_in;
00321         struct sockaddr_in6 address_in6;
00322         socklen_t addrlen;
00323 
00324         int listening_id;
00325         int index;
00326         int rc;
00327 
00328         /* Parse command line arguments */
00329         for (index = 1; index < argc; ++index) {
00330                 if (argv[index][0] == '-') {
00331                         rc = netecho_parse_option(argc, argv, &index);
00332                         if (rc != EOK)
00333                                 return rc;
00334                 } else {
00335                         echo_print_help();
00336                         return EINVAL;
00337                 }
00338         }
00339 
00340         /* Check buffer size */
00341         if (size <= 0) {
00342                 fprintf(stderr, "Receive size too small (%zu). Using 1024 bytes instead.\n", size);
00343                 size = 1024;
00344         }
00345 
00346         /* size plus the terminating null character. */
00347         data = (char *) malloc(size + 1);
00348         if (!data) {
00349                 fprintf(stderr, "Failed to allocate receive buffer.\n");
00350                 return ENOMEM;
00351         }
00352 
00353         /* Set the reply size if set */
00354         reply_length = reply ? str_length(reply) : 0;
00355 
00356         /* Prepare the address buffer */
00357         switch (family) {
00358         case PF_INET:
00359                 address_in.sin_family = AF_INET;
00360                 address_in.sin_port = htons(port);
00361                 address = (struct sockaddr *) &address_in;
00362                 addrlen = sizeof(address_in);
00363                 break;
00364         case PF_INET6:
00365                 address_in6.sin6_family = AF_INET6;
00366                 address_in6.sin6_port = htons(port);
00367                 address = (struct sockaddr *) &address_in6;
00368                 addrlen = sizeof(address_in6);
00369                 break;
00370         default:
00371                 fprintf(stderr, "Protocol family is not supported\n");
00372                 return EAFNOSUPPORT;
00373         }
00374 
00375         /* Get a listening socket */
00376         listening_id = socket(family, type, 0);
00377         if (listening_id < 0) {
00378                 socket_print_error(stderr, listening_id, "Socket create: ", "\n");
00379                 return listening_id;
00380         }
00381 
00382         /* if the stream socket is used */
00383         if (type == SOCK_STREAM) {
00384                 /* Check backlog size */
00385                 if (backlog <= 0) {
00386                         fprintf(stderr, "Accepted sockets queue size too small (%zu). Using 3 instead.\n", size);
00387                         backlog = 3;
00388                 }
00389 
00390                 /* Set the backlog */
00391                 rc = listen(listening_id, backlog);
00392                 if (rc != EOK) {
00393                         socket_print_error(stderr, rc, "Socket listen: ", "\n");
00394                         return rc;
00395                 }
00396         }
00397 
00398         /* Bind the listening socket */
00399         rc = bind(listening_id, address, addrlen);
00400         if (rc != EOK) {
00401                 socket_print_error(stderr, rc, "Socket bind: ", "\n");
00402                 return rc;
00403         }
00404 
00405         if (verbose)
00406                 printf("Socket %d listenning at %d\n", listening_id, port);
00407 
00408         /*
00409          * do count times
00410          * or indefinitely if set to a negative value
00411          */
00412         while (count) {
00413                 rc = netecho_socket_process_message(listening_id);
00414                 if (rc != EOK)
00415                         break;
00416 
00417                 /* Decrease count if positive */
00418                 if (count > 0) {
00419                         count--;
00420                         if (verbose)
00421                                 printf("Waiting for next %d message(s)\n", count);
00422                 }
00423         }
00424 
00425         if (verbose)
00426                 printf("Closing the socket\n");
00427 
00428         /* Close listenning socket */
00429         rc = closesocket(listening_id);
00430         if (rc != EOK) {
00431                 socket_print_error(stderr, rc, "Close socket: ", "\n");
00432                 return rc;
00433         }
00434 
00435         if (verbose)
00436                 printf("Exiting\n");
00437 
00438         return EOK;
00439 }
00440 

Generated on Thu Jun 2 07:45:42 2011 for HelenOS/USB by  doxygen 1.4.7