Annotation of embedaddon/mtr/packet/command.c, revision 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>