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

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

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