Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/netecho/netecho.c

    ra62ceaf rc442f63  
    11/*
    2  * Copyright (c) 2016 Jiri Svoboda
     2 * Copyright (c) 2009 Lukas Mejdrech
    33 * All rights reserved.
    44 *
     
    3030 * @{
    3131 */
    32 /** @file Network UDP echo diagnostic utility.
     32
     33/** @file
     34 * Network echo server.
     35 *
     36 * Sockets-based server that echoes incomming messages. If stream mode
     37 * is selected, accepts incoming connections.
    3338 */
    3439
    35 #include <stdbool.h>
    36 #include <errno.h>
    37 #include <io/console.h>
     40#include <assert.h>
    3841#include <stdio.h>
     42#include <stdlib.h>
    3943#include <str.h>
    40 
    41 #include "comm.h"
    42 #include "netecho.h"
     44#include <task.h>
     45#include <arg_parse.h>
     46
     47#include <net/in.h>
     48#include <net/in6.h>
     49#include <net/inet.h>
     50#include <net/socket.h>
     51#include <net/socket_parse.h>
     52
     53#include "print_error.h"
    4354
    4455#define NAME "netecho"
    4556
    46 static console_ctrl_t *con;
    47 static bool done;
    48 
    49 void netecho_received(void *data, size_t size)
     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;
     66static size_t reply_length;
     67
     68static char *data;
     69
     70static void echo_print_help(void)
    5071{
    51         char *p;
    52         size_t i;
    53 
    54         printf("Received message '");
    55         p = data;
    56 
    57         for (i = 0; i < size; i++)
    58                 putchar(p[i]);
    59         printf("'.\n");
     72        printf(
     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"
     109        );
    60110}
    61111
    62 static void key_handle_ctrl(kbd_event_t *ev)
     112static int netecho_parse_option(int argc, char *argv[], int *index)
    63113{
    64         switch (ev->key) {
    65         case KC_Q:
    66                 done = true;
     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();
     182                        exit(0);
     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                }
    67209                break;
    68210        default:
    69                 break;
    70         }
     211                echo_print_help();
     212                return EINVAL;
     213        }
     214
     215        return EOK;
    71216}
    72217
    73 static void send_char(wchar_t c)
     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 */
     223static int netecho_socket_process_message(int listening_id)
    74224{
    75         char cbuf[STR_BOUNDS(1)];
    76         size_t offs;
     225        uint8_t address_buf[sizeof(struct sockaddr_in6)];
     226
     227        socklen_t addrlen = sizeof(struct sockaddr_in6);
     228        int socket_id;
     229        ssize_t rcv_size;
     230        size_t length;
     231        uint8_t *address_start;
     232
     233        char address_string[INET6_ADDRSTRLEN];
     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
    77238        int rc;
    78239
    79         offs = 0;
    80         chr_encode(c, cbuf, &offs, STR_BOUNDS(1));
    81 
    82         rc = comm_send(cbuf, offs);
    83         if (rc != EOK) {
    84                 printf("[Failed sending data]\n");
    85         }
     240        if (type == SOCK_STREAM) {
     241                /* Accept a socket if a stream socket is used */
     242                if (verbose)
     243                        printf("accept()\n");
     244                socket_id = accept(listening_id, (void *) address_buf, &addrlen);
     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                }
     251
     252                assert((size_t) addrlen <= sizeof(address_buf));
     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 */
     261                if (verbose)
     262                        printf("recvfrom()\n");
     263                rcv_size = recvfrom(socket_id, data, size, 0, address,
     264                    &addrlen);
     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);
     281                                        address_start = (uint8_t *) address_in6->sin6_addr.s6_addr;
     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 */
     302                        if (type == SOCK_STREAM) {
     303                                if (verbose)
     304                                        printf("send()\n");
     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 {
     309                                if (verbose)
     310                                        printf("sendto()\n");
     311                                rc = sendto(socket_id, reply ? reply : data, reply ? reply_length : length, 0, address, addrlen);
     312                                if (rc != EOK)
     313                                        socket_print_error(stderr, rc, "Socket sendto: ", "\n");
     314                        }
     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;
    86327}
    87328
    88 static void key_handle_unmod(kbd_event_t *ev)
    89 {
    90         switch (ev->key) {
    91         case KC_ENTER:
    92                 send_char('\n');
    93                 break;
    94         default:
    95                 if (ev->c >= 32 || ev->c == '\t' || ev->c == '\b') {
    96                         send_char(ev->c);
    97                 }
    98         }
    99 }
    100 
    101 static void key_handle(kbd_event_t *ev)
    102 {
    103         if ((ev->mods & KM_ALT) == 0 &&
    104             (ev->mods & KM_SHIFT) == 0 &&
    105             (ev->mods & KM_CTRL) != 0) {
    106                 key_handle_ctrl(ev);
    107         } else if ((ev->mods & (KM_CTRL | KM_ALT)) == 0) {
    108                 key_handle_unmod(ev);
    109         }
    110 }
    111 
    112 
    113 static void print_syntax(void)
    114 {
    115         printf("syntax:\n");
    116         printf("\t%s -l <port>\n", NAME);
    117         printf("\t%s -d <host>:<port> [<message> [<message...>]]\n", NAME);
    118 }
    119 
    120 /* Interactive mode */
    121 static void netecho_interact(void)
    122 {
    123         cons_event_t ev;
    124 
    125         printf("Communication started. Press Ctrl-Q to quit.\n");
    126 
    127         con = console_init(stdin, stdout);
    128 
    129         done = false;
    130         while (!done) {
    131                 console_get_event(con, &ev);
    132                 if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS)
    133                         key_handle(&ev.ev.key);
    134         }
    135 }
    136 
    137 static void netecho_send_messages(char **msgs)
    138 {
    139         int rc;
    140 
    141         while (*msgs != NULL) {
    142                 rc = comm_send(*msgs, str_size(*msgs));
    143                 if (rc != EOK) {
    144                         printf("[Failed sending data]\n");
    145                 }
    146 
    147                 ++msgs;
    148         }
    149 }
    150329
    151330int main(int argc, char *argv[])
    152331{
    153         char *hostport;
    154         char *port;
    155         char **msgs;
     332        struct sockaddr *address;;
     333        struct sockaddr_in address_in;
     334        struct sockaddr_in6 address_in6;
     335        socklen_t addrlen;
     336
     337        int listening_id;
     338        int index;
    156339        int rc;
    157340
    158         if (argc < 2) {
    159                 print_syntax();
    160                 return 1;
    161         }
    162 
    163         if (str_cmp(argv[1], "-l") == 0) {
    164                 if (argc != 3) {
    165                         print_syntax();
    166                         return 1;
    167                 }
    168 
    169                 port = argv[2];
    170                 msgs = NULL;
    171 
    172                 rc = comm_open_listen(port);
     341        /* Parse command line arguments */
     342        for (index = 1; index < argc; ++index) {
     343                if (argv[index][0] == '-') {
     344                        rc = netecho_parse_option(argc, argv, &index);
     345                        if (rc != EOK)
     346                                return rc;
     347                } else {
     348                        echo_print_help();
     349                        return EINVAL;
     350                }
     351        }
     352
     353        /* Check buffer size */
     354        if (size <= 0) {
     355                fprintf(stderr, "Receive size too small (%zu). Using 1024 bytes instead.\n", size);
     356                size = 1024;
     357        }
     358
     359        /* size plus the terminating null character. */
     360        data = (char *) malloc(size + 1);
     361        if (!data) {
     362                fprintf(stderr, "Failed to allocate receive buffer.\n");
     363                return ENOMEM;
     364        }
     365
     366        /* Set the reply size if set */
     367        reply_length = reply ? str_length(reply) : 0;
     368
     369        /* Prepare the address buffer */
     370        switch (family) {
     371        case PF_INET:
     372                address_in.sin_family = AF_INET;
     373                address_in.sin_port = htons(port);
     374                address_in.sin_addr.s_addr = INADDR_ANY;
     375                address = (struct sockaddr *) &address_in;
     376                addrlen = sizeof(address_in);
     377                break;
     378        case PF_INET6:
     379                address_in6.sin6_family = AF_INET6;
     380                address_in6.sin6_port = htons(port);
     381                address_in6.sin6_addr = in6addr_any;
     382                address = (struct sockaddr *) &address_in6;
     383                addrlen = sizeof(address_in6);
     384                break;
     385        default:
     386                fprintf(stderr, "Protocol family is not supported\n");
     387                return EAFNOSUPPORT;
     388        }
     389
     390        /* Get a listening socket */
     391        listening_id = socket(family, type, 0);
     392        if (listening_id < 0) {
     393                socket_print_error(stderr, listening_id, "Socket create: ", "\n");
     394                return listening_id;
     395        }
     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       
     404        /* if the stream socket is used */
     405        if (type == SOCK_STREAM) {
     406                /* Check backlog size */
     407                if (backlog <= 0) {
     408                        fprintf(stderr, "Accepted sockets queue size too small (%zu). Using 3 instead.\n", size);
     409                        backlog = 3;
     410                }
     411               
     412                /* Set the backlog */
     413                rc = listen(listening_id, backlog);
    173414                if (rc != EOK) {
    174                         printf("Error setting up communication.\n");
    175                         return 1;
    176                 }
    177         } else if (str_cmp(argv[1], "-d") == 0) {
    178                 if (argc < 3) {
    179                         print_syntax();
    180                         return 1;
    181                 }
    182 
    183                 hostport = argv[2];
    184                 port = NULL;
    185                 msgs = argv + 3;
    186 
    187                 rc = comm_open_talkto(hostport);
    188                 if (rc != EOK) {
    189                         printf("Error setting up communication.\n");
    190                         return 1;
    191                 }
    192         } else {
    193                 print_syntax();
    194                 return 1;
    195         }
    196 
    197         if (msgs != NULL && *msgs != NULL) {
    198                 /* Just send messages and quit */
    199                 netecho_send_messages(msgs);
    200         } else {
    201                 /* Interactive mode */
    202                 netecho_interact();
    203         }
    204 
    205         comm_close();
    206         return 0;
     415                        socket_print_error(stderr, rc, "Socket listen: ", "\n");
     416                        return rc;
     417                }
     418        }
     419
     420        if (verbose)
     421                printf("Socket %d listenning at %d\n", listening_id, port);
     422
     423        /*
     424         * do count times
     425         * or indefinitely if set to a negative value
     426         */
     427        while (count) {
     428                rc = netecho_socket_process_message(listening_id);
     429                if (rc != EOK)
     430                        break;
     431
     432                /* Decrease count if positive */
     433                if (count > 0) {
     434                        count--;
     435                        if (verbose)
     436                                printf("Waiting for next %d message(s)\n", count);
     437                }
     438        }
     439
     440        if (verbose)
     441                printf("Closing the socket\n");
     442
     443        /* Close listenning socket */
     444        rc = closesocket(listening_id);
     445        if (rc != EOK) {
     446                socket_print_error(stderr, rc, "Close socket: ", "\n");
     447                return rc;
     448        }
     449
     450        if (verbose)
     451                printf("Exiting\n");
     452
     453        return EOK;
    207454}
    208455
Note: See TracChangeset for help on using the changeset viewer.