Annotation of embedaddon/mtr/packet/command.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:     mtr  --  a network diagnostic tool
                      3:     Copyright (C) 2016  Matt Kimball
                      4: 
                      5:     This program is free software; you can redistribute it and/or modify
                      6:     it under the terms of the GNU General Public License version 2 as
                      7:     published by the Free Software Foundation.
                      8: 
                      9:     This program is distributed in the hope that it will be useful,
                     10:     but WITHOUT ANY WARRANTY; without even the implied warranty of
                     11:     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     12:     GNU General Public License for more details.
                     13: 
1.1.1.2   misho      14:     You should have received a copy of the GNU General Public License along
                     15:     with this program; if not, write to the Free Software Foundation, Inc.,
                     16:     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.1       misho      17: */
                     18: 
                     19: #include "command.h"
                     20: 
                     21: #include <assert.h>
                     22: #include <errno.h>
1.1.1.2   misho      23: #ifdef HAVE_ERROR_H
                     24: #include <error.h>
                     25: #else
                     26: #include "portability/error.h"
                     27: #endif
                     28: #include <fcntl.h>
1.1       misho      29: #include <stdbool.h>
                     30: #include <stdio.h>
                     31: #include <stdlib.h>
                     32: #include <string.h>
                     33: #include <strings.h>
1.1.1.2   misho      34: #include <unistd.h>
1.1       misho      35: 
                     36: #include "cmdparse.h"
                     37: #include "platform.h"
                     38: #include "config.h"
                     39: 
                     40: /*
                     41:     Find a parameter with a particular name in a command_t structure.
                     42:     If no such parameter exists, return NULL.
                     43: */
                     44: static
                     45: const char *find_parameter(
                     46:     const struct command_t *command,
                     47:     const char *name_request)
                     48: {
                     49:     const char *name;
                     50:     const char *value;
                     51:     int i;
                     52: 
                     53:     for (i = 0; i < command->argument_count; i++) {
                     54:         name = command->argument_name[i];
                     55:         value = command->argument_value[i];
                     56: 
                     57:         if (!strcmp(name, name_request)) {
                     58:             return value;
                     59:         }
                     60:     }
                     61: 
                     62:     return NULL;
                     63: }
                     64: 
                     65: /*  Returns a feature support string for a particular probe protocol  */
                     66: static
                     67: const char *check_protocol_support(
                     68:     struct net_state_t *net_state,
                     69:     int protocol)
                     70: {
                     71:     if (is_protocol_supported(net_state, protocol)) {
                     72:         return "ok";
                     73:     } else {
                     74:         return "no";
                     75:     }
                     76: }
                     77: 
                     78: /*  Return a feature support string for an IP protocol version  */
                     79: static
                     80: const char *check_ip_version_support(
                     81:     struct net_state_t *net_state,
                     82:     int ip_version)
                     83: {
                     84:     if (is_ip_version_supported(net_state, ip_version)) {
                     85:         return "ok";
                     86:     } else {
                     87:         return "no";
                     88:     }
                     89: }
                     90: 
                     91: /*  Given a feature name, return a string for the check-support reply  */
                     92: static
                     93: const char *check_support(
                     94:     const char *feature,
                     95:     struct net_state_t *net_state)
                     96: {
                     97:     if (!strcmp(feature, "version")) {
                     98:         return PACKAGE_VERSION;
                     99:     }
                    100: 
                    101:     if (!strcmp(feature, "ip-4")) {
                    102:         return check_ip_version_support(net_state, 4);
                    103:     }
                    104: 
                    105:     if (!strcmp(feature, "ip-6")) {
                    106:         return check_ip_version_support(net_state, 6);
                    107:     }
                    108: 
                    109:     if (!strcmp(feature, "send-probe")) {
                    110:         return "ok";
                    111:     }
                    112: 
                    113:     if (!strcmp(feature, "icmp")) {
                    114:         return check_protocol_support(net_state, IPPROTO_ICMP);
                    115:     }
                    116: 
                    117:     if (!strcmp(feature, "udp")) {
                    118:         return check_protocol_support(net_state, IPPROTO_UDP);
                    119:     }
                    120: 
                    121:     if (!strcmp(feature, "tcp")) {
                    122:         return check_protocol_support(net_state, IPPROTO_TCP);
                    123:     }
                    124: #ifdef IPPROTO_SCTP
                    125:     if (!strcmp(feature, "sctp")) {
                    126:         return check_protocol_support(net_state, IPPROTO_SCTP);
                    127:     }
                    128: #endif
                    129: 
                    130: #ifdef SO_MARK
                    131:     if (!strcmp(feature, "mark")) {
                    132:         return "ok";
                    133:     }
                    134: #endif
                    135: 
                    136:     return "no";
                    137: }
                    138: 
                    139: /*  Handle a check-support request by checking for a particular feature  */
                    140: static
                    141: void check_support_command(
                    142:     const struct command_t *command,
                    143:     struct net_state_t *net_state)
                    144: {
                    145:     const char *feature;
                    146:     const char *support;
                    147: 
                    148:     feature = find_parameter(command, "feature");
                    149:     if (feature == NULL) {
                    150:         printf("%d invalid-argument\n", command->token);
                    151:         return;
                    152:     }
                    153: 
                    154:     support = check_support(feature, net_state);
                    155:     printf("%d feature-support support %s\n", command->token, support);
                    156: }
                    157: 
                    158: /*
                    159:     If a named send_probe argument is recognized, fill in the probe paramters
                    160:     structure with the argument value.
                    161: */
                    162: static
                    163: bool decode_probe_argument(
                    164:     struct probe_param_t *param,
                    165:     const char *name,
                    166:     const char *value)
                    167: {
                    168:     char *endstr = NULL;
                    169: 
                    170:     /*  Pass IPv4 addresses as string values  */
                    171:     if (!strcmp(name, "ip-4")) {
                    172:         param->ip_version = 4;
                    173:         param->remote_address = value;
                    174:     }
                    175: 
                    176:     /*  IPv6 address  */
                    177:     if (!strcmp(name, "ip-6")) {
                    178:         param->ip_version = 6;
                    179:         param->remote_address = value;
                    180:     }
                    181: 
                    182:     /*  IPv4 address to send from  */
                    183:     if (!strcmp(name, "local-ip-4")) {
                    184:         param->local_address = value;
                    185:     }
                    186: 
                    187:     /*  IPv6 address to send from  */
                    188:     if (!strcmp(name, "local-ip-6")) {
                    189:         param->local_address = value;
                    190:     }
                    191: 
                    192:     /*  Protocol for the probe  */
                    193:     if (!strcmp(name, "protocol")) {
                    194:         if (!strcmp(value, "icmp")) {
                    195:             param->protocol = IPPROTO_ICMP;
                    196:         } else if (!strcmp(value, "udp")) {
                    197:             param->protocol = IPPROTO_UDP;
                    198:         } else if (!strcmp(value, "tcp")) {
                    199:             param->protocol = IPPROTO_TCP;
                    200: #ifdef IPPROTO_SCTP
                    201:         } else if (!strcmp(value, "sctp")) {
                    202:             param->protocol = IPPROTO_SCTP;
                    203: #endif
                    204:         } else {
                    205:             return false;
                    206:         }
                    207:     }
                    208: 
                    209:     /*  Destination port for the probe  */
                    210:     if (!strcmp(name, "port")) {
                    211:         param->dest_port = strtol(value, &endstr, 10);
                    212:         if (*endstr != 0) {
                    213:             return false;
                    214:         }
                    215:     }
                    216: 
                    217:     /*  The local port to send UDP probes from  */
                    218:     if (!strcmp(name, "local-port")) {
                    219:         param->local_port = strtol(value, &endstr, 10);
                    220:         if (*endstr != 0) {
                    221:             return false;
                    222:         }
                    223: 
                    224:         /*
                    225:            Don't allow using a local port which requires
                    226:            privileged binding.
                    227:          */
                    228:         if (param->local_port < 1024) {
                    229:             param->local_port = 0;
                    230:             return false;
                    231:         }
                    232:     }
                    233: 
                    234:     /*  The "type of service" field for the IP header  */
                    235:     if (!strcmp(name, "tos")) {
                    236:         param->type_of_service = strtol(value, &endstr, 10);
                    237:         if (*endstr != 0) {
                    238:             return false;
                    239:         }
                    240:     }
                    241: 
                    242:     /*  The Linux packet mark for mark-based routing  */
                    243:     if (!strcmp(name, "mark")) {
                    244:         param->routing_mark = strtol(value, &endstr, 10);
                    245:         if (*endstr != 0) {
                    246:             return false;
                    247:         }
                    248:     }
                    249: 
                    250:     /*  The size of the packet (including headers)  */
                    251:     if (!strcmp(name, "size")) {
                    252:         param->packet_size = strtol(value, &endstr, 10);
                    253:         if (*endstr != 0) {
                    254:             return false;
                    255:         }
                    256:     }
                    257: 
                    258:     /*  The packet's bytes will be filled with this value  */
                    259:     if (!strcmp(name, "bit-pattern")) {
                    260:         param->bit_pattern = strtol(value, &endstr, 10);
                    261:         if (*endstr != 0) {
                    262:             return false;
                    263:         }
                    264:     }
                    265: 
                    266:     /*  Time-to-live values  */
                    267:     if (!strcmp(name, "ttl")) {
                    268:         param->ttl = strtol(value, &endstr, 10);
                    269:         if (*endstr != 0) {
                    270:             return false;
                    271:         }
                    272:     }
                    273: 
                    274:     /*  Number of seconds to wait for a reply  */
                    275:     if (!strcmp(name, "timeout")) {
                    276:         param->timeout = strtol(value, &endstr, 10);
                    277:         if (*endstr != 0) {
                    278:             return false;
                    279:         }
                    280:     }
                    281: 
                    282:     return true;
                    283: }
                    284: 
                    285: /*
                    286:     Check the probe parameters against currently supported features
                    287:     and report and error if there is a problem.
                    288: 
                    289:     Return true if everything is okay, false otherwise.
                    290: */
                    291: static
                    292: bool validate_probe_parameters(
                    293:     struct net_state_t *net_state,
                    294:     struct probe_param_t *param)
                    295: {
                    296:     if (!is_ip_version_supported(net_state, param->ip_version)) {
                    297:         printf("%d invalid-argument reason ip-version-not-supported\n",
                    298:                param->command_token);
                    299: 
                    300:         return false;
                    301:     }
                    302: 
                    303:     if (!is_protocol_supported(net_state, param->protocol)) {
                    304:         printf("%d invalid-argument reason protocol-not-supported\n",
                    305:                param->command_token);
                    306: 
                    307:         return false;
                    308:     }
                    309: 
                    310:     return true;
                    311: }
                    312: 
                    313: /*  Handle "send-probe" commands  */
                    314: static
                    315: void send_probe_command(
                    316:     const struct command_t *command,
                    317:     struct net_state_t *net_state)
                    318: {
                    319:     struct probe_param_t param;
                    320:     int i;
                    321:     char *name;
                    322:     char *value;
                    323: 
                    324:     /*  We will prepare a probe_param_t for send_probe.  */
                    325:     memset(&param, 0, sizeof(struct probe_param_t));
                    326:     param.command_token = command->token;
                    327:     param.protocol = IPPROTO_ICMP;
                    328:     param.ttl = 255;
                    329:     param.packet_size = 64;
                    330:     param.timeout = 10;
1.1.1.2   misho     331:     param.is_probing_byte_order = false;
1.1       misho     332: 
                    333:     for (i = 0; i < command->argument_count; i++) {
                    334:         name = command->argument_name[i];
                    335:         value = command->argument_value[i];
                    336: 
                    337:         if (!decode_probe_argument(&param, name, value)) {
                    338:             printf("%d invalid-argument\n", command->token);
                    339:             return;
                    340:         }
                    341:     }
                    342: 
                    343:     if (!validate_probe_parameters(net_state, &param)) {
                    344:         return;
                    345:     }
                    346: 
                    347:     /*  Send the probe using a platform specific mechanism  */
                    348:     send_probe(net_state, &param);
                    349: }
                    350: 
                    351: /*
                    352:     Given a parsed command, dispatch to the handler for specific
                    353:     command requests.
                    354: */
                    355: static
                    356: void dispatch_command(
                    357:     const struct command_t *command,
                    358:     struct net_state_t *net_state)
                    359: {
                    360:     if (!strcmp(command->command_name, "check-support")) {
                    361:         check_support_command(command, net_state);
                    362:     } else if (!strcmp(command->command_name, "send-probe")) {
                    363:         send_probe_command(command, net_state);
                    364:     } else {
                    365:         /*  For unrecognized commands, respond with an error  */
                    366:         printf("%d unknown-command\n", command->token);
                    367:     }
                    368: }
                    369: 
                    370: /*
                    371:     With newly read data in our command buffer, dispatch all completed
                    372:     command requests.
                    373: */
                    374: void dispatch_buffer_commands(
                    375:     struct command_buffer_t *buffer,
                    376:     struct net_state_t *net_state)
                    377: {
                    378:     struct command_t command;
                    379:     char *end_of_command;
                    380:     char full_command[COMMAND_BUFFER_SIZE];
                    381:     int command_length;
                    382:     int remaining_count;
                    383: 
                    384:     while (true) {
                    385:         assert(buffer->incoming_read_position < COMMAND_BUFFER_SIZE);
                    386: 
                    387:         /*  Terminate the buffer string  */
                    388:         buffer->incoming_buffer[buffer->incoming_read_position] = 0;
                    389: 
                    390:         /*  Find the next newline, which terminates command requests  */
                    391:         end_of_command = index(buffer->incoming_buffer, '\n');
                    392:         if (end_of_command == NULL) {
                    393:             /*
                    394:                No newlines found, so any data we've read so far is
                    395:                not yet complete.
                    396:              */
                    397:             break;
                    398:         }
                    399: 
                    400:         command_length = end_of_command - buffer->incoming_buffer;
                    401:         remaining_count =
                    402:             buffer->incoming_read_position - command_length - 1;
                    403: 
                    404:         /*  Copy the completed command  */
                    405:         memmove(full_command, buffer->incoming_buffer, command_length);
                    406:         full_command[command_length] = 0;
                    407: 
                    408:         /*
                    409:            Free the space used by the completed command by advancing the
                    410:            remaining requests within the buffer.
                    411:          */
                    412:         memmove(buffer->incoming_buffer, end_of_command + 1,
                    413:                 remaining_count);
                    414:         buffer->incoming_read_position -= command_length + 1;
                    415: 
                    416:         if (parse_command(&command, full_command)) {
                    417:             /*  If the command fails to parse, respond with an error  */
                    418:             printf("0 command-parse-error\n");
                    419:         } else {
                    420:             dispatch_command(&command, net_state);
                    421:         }
                    422:     }
                    423: 
                    424:     if (buffer->incoming_read_position >= COMMAND_BUFFER_SIZE - 1) {
                    425:         /*
                    426:            If we've filled the buffer without a complete command, the
1.1.1.3 ! misho     427:            only thing we can do is discard what we've read and hope that
1.1       misho     428:            new data is better formatted.
                    429:          */
                    430:         printf("0 command-buffer-overflow\n");
                    431:         buffer->incoming_read_position = 0;
                    432:     }
                    433: }
1.1.1.2   misho     434: 
                    435: /*
                    436:     Initialize the command buffer and put the command stream in
                    437:     non-blocking mode.
                    438: */
                    439: void init_command_buffer(
                    440:     struct command_buffer_t *command_buffer,
                    441:     int command_stream)
                    442: {
                    443:     int flags;
                    444: 
                    445:     memset(command_buffer, 0, sizeof(struct command_buffer_t));
                    446:     command_buffer->command_stream = command_stream;
                    447: 
                    448:     /*  Get the current command stream flags  */
                    449:     flags = fcntl(command_stream, F_GETFL, 0);
                    450:     if (flags == -1) {
                    451:         error(EXIT_FAILURE, errno, "Unexpected command stream error");
                    452:     }
                    453: 
                    454:     /*  Set the O_NONBLOCK bit  */
                    455:     if (fcntl(command_stream, F_SETFL, flags | O_NONBLOCK)) {
                    456:         error(EXIT_FAILURE, errno, "Unexpected command stream error");
                    457:     }
                    458: }
                    459: 
                    460: /*  Read currently available data from the command stream  */
                    461: int read_commands(
                    462:     struct command_buffer_t *buffer)
                    463: {
                    464:     int space_remaining =
                    465:         COMMAND_BUFFER_SIZE - buffer->incoming_read_position - 1;
                    466:     char *read_position =
                    467:         &buffer->incoming_buffer[buffer->incoming_read_position];
                    468:     int read_count;
                    469:     int command_stream = buffer->command_stream;
                    470: 
                    471:     read_count = read(command_stream, read_position, space_remaining);
                    472: 
                    473:     /*  If the command stream has been closed, read will return zero.  */
                    474:     if (read_count == 0) {
                    475:         errno = EPIPE;
                    476:         return -1;
                    477:     }
                    478: 
                    479:     if (read_count > 0) {
                    480:         /*  Account for the newly read data  */
                    481:         buffer->incoming_read_position += read_count;
                    482:     }
                    483: 
                    484:     if (read_count < 0) {
                    485:         /*  EAGAIN simply means there is no available data to read  */
                    486:         /*  EINTR indicates we received a signal during read  */
                    487:         if (errno != EINTR && errno != EAGAIN) {
                    488:             error(EXIT_FAILURE, errno, "Unexpected command buffer read error");
                    489:         }
                    490:     }
                    491: 
                    492:     return 0;
                    493: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>