Annotation of embedaddon/mtr/ui/cmdpipe.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 "cmdpipe.h"
        !            20: 
        !            21: #include <assert.h>
        !            22: #include <errno.h>
        !            23: #include <fcntl.h>
        !            24: #include <signal.h>
        !            25: #include <stdbool.h>
        !            26: #include <stdio.h>
        !            27: #include <stdlib.h>
        !            28: #include <string.h>
        !            29: #include <strings.h>
        !            30: #include <sys/wait.h>
        !            31: #include <unistd.h>
        !            32: 
        !            33: #ifdef HAVE_ERROR_H
        !            34: #include <error.h>
        !            35: #else
        !            36: #include "portability/error.h"
        !            37: #endif
        !            38: 
        !            39: #include "packet/cmdparse.h"
        !            40: #include "display.h"
        !            41: 
        !            42: 
        !            43: /*  Set a file descriptor to non-blocking  */
        !            44: static
        !            45: void set_fd_nonblock(
        !            46:     int fd)
        !            47: {
        !            48:     int flags;
        !            49: 
        !            50:     /*  Get the current flags of the file descriptor  */
        !            51:     flags = fcntl(fd, F_GETFL, 0);
        !            52:     if (flags == -1) {
        !            53:         error(EXIT_FAILURE, errno, "F_GETFL failure");
        !            54:         exit(1);
        !            55:     }
        !            56: 
        !            57:     /*  Add the O_NONBLOCK bit to the current flags  */
        !            58:     if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        !            59:         error(EXIT_FAILURE, errno, "Failure to set O_NONBLOCK");
        !            60:         exit(1);
        !            61:     }
        !            62: }
        !            63: 
        !            64: 
        !            65: /*
        !            66:     Send a command synchronously to mtr-packet, blocking until a result
        !            67:     is available.  This is intended to be used at start-up to check the
        !            68:     capabilities of mtr-packet, but probes should be sent asynchronously
        !            69:     to avoid blocking other probes and the user interface.
        !            70: */
        !            71: static
        !            72: int send_synchronous_command(
        !            73:     struct mtr_ctl *ctl,
        !            74:     struct packet_command_pipe_t *cmdpipe,
        !            75:     const char *cmd,
        !            76:     struct command_t *result)
        !            77: {
        !            78:     char reply[PACKET_REPLY_BUFFER_SIZE];
        !            79:     int command_length;
        !            80:     int write_length;
        !            81:     int read_length;
        !            82: 
        !            83:     /*  Query send-probe support  */
        !            84:     command_length = strlen(cmd);
        !            85:     write_length = write(cmdpipe->write_fd, cmd, command_length);
        !            86: 
        !            87:     if (write_length == -1) {
        !            88:         return -1;
        !            89:     }
        !            90: 
        !            91:     if (write_length != command_length) {
        !            92:         errno = EIO;
        !            93:         return -1;
        !            94:     }
        !            95: 
        !            96:     /*  Read the reply to our query  */
        !            97:     read_length =
        !            98:         read(cmdpipe->read_fd, reply, PACKET_REPLY_BUFFER_SIZE - 1);
        !            99: 
        !           100:     if (read_length < 0) {
        !           101:         return -1;
        !           102:     }
        !           103: 
        !           104:     /*  Parse the query reply  */
        !           105:     reply[read_length] = 0;
        !           106:     if (parse_command(result, reply)) {
        !           107:         return -1;
        !           108:     }
        !           109: 
        !           110:     return 0;
        !           111: }
        !           112: 
        !           113: 
        !           114: /*  Check support for a particular feature with the mtr-packet we invoked  */
        !           115: static
        !           116: int check_feature(
        !           117:     struct mtr_ctl *ctl,
        !           118:     struct packet_command_pipe_t *cmdpipe,
        !           119:     const char *feature)
        !           120: {
        !           121:     char check_command[COMMAND_BUFFER_SIZE];
        !           122:     struct command_t reply;
        !           123: 
        !           124:     snprintf(check_command, COMMAND_BUFFER_SIZE,
        !           125:              "1 check-support feature %s\n", feature);
        !           126: 
        !           127:     if (send_synchronous_command(ctl, cmdpipe, check_command, &reply) ==
        !           128:         -1) {
        !           129:         return -1;
        !           130:     }
        !           131: 
        !           132:     /*  Check that the feature is supported  */
        !           133:     if (!strcmp(reply.command_name, "feature-support")
        !           134:         && reply.argument_count >= 1
        !           135:         && !strcmp(reply.argument_name[0], "support")
        !           136:         && !strcmp(reply.argument_value[0], "ok")) {
        !           137: 
        !           138:         /*  Looks good  */
        !           139:         return 0;
        !           140:     }
        !           141: 
        !           142:     errno = ENOTSUP;
        !           143:     return -1;
        !           144: }
        !           145: 
        !           146: 
        !           147: /*
        !           148:     Check the protocol selected against the mtr-packet we are using.
        !           149:     Returns zero if everything is fine, or -1 with errno for either
        !           150:     a failure during the check, or for an unsupported feature.
        !           151: */
        !           152: static
        !           153: int check_packet_features(
        !           154:     struct mtr_ctl *ctl,
        !           155:     struct packet_command_pipe_t *cmdpipe)
        !           156: {
        !           157:     /*  Check the IP protocol version  */
        !           158:     if (ctl->af == AF_INET6) {
        !           159:         if (check_feature(ctl, cmdpipe, "ip-6")) {
        !           160:             return -1;
        !           161:         }
        !           162:     } else if (ctl->af == AF_INET) {
        !           163:         if (check_feature(ctl, cmdpipe, "ip-4")) {
        !           164:             return -1;
        !           165:         }
        !           166:     } else {
        !           167:         errno = EINVAL;
        !           168:         return -1;
        !           169:     }
        !           170: 
        !           171:     /*  Check the transport protocol  */
        !           172:     if (ctl->mtrtype == IPPROTO_ICMP) {
        !           173:         if (check_feature(ctl, cmdpipe, "icmp")) {
        !           174:             return -1;
        !           175:         }
        !           176:     } else if (ctl->mtrtype == IPPROTO_UDP) {
        !           177:         if (check_feature(ctl, cmdpipe, "udp")) {
        !           178:             return -1;
        !           179:         }
        !           180:     } else if (ctl->mtrtype == IPPROTO_TCP) {
        !           181:         if (check_feature(ctl, cmdpipe, "tcp")) {
        !           182:             return -1;
        !           183:         }
        !           184: #ifdef HAS_SCTP
        !           185:     } else if (ctl->mtrtype == IPPROTO_SCTP) {
        !           186:         if (check_feature(ctl, cmdpipe, "sctp")) {
        !           187:             return -1;
        !           188:         }
        !           189: #endif
        !           190:     } else {
        !           191:         errno = EINVAL;
        !           192:         return -1;
        !           193:     }
        !           194: 
        !           195: #ifdef SO_MARK
        !           196:     if (ctl->mark) {
        !           197:         if (check_feature(ctl, cmdpipe, "mark")) {
        !           198:             return -1;
        !           199:         }
        !           200:     }
        !           201: #endif
        !           202: 
        !           203:     return 0;
        !           204: }
        !           205: 
        !           206: 
        !           207: /*
        !           208:     Execute mtr-packet, allowing the MTR_PACKET evironment to override
        !           209:     the PATH when locating the executable.
        !           210: */
        !           211: static
        !           212: void execute_packet_child(
        !           213:     void)
        !           214: {
        !           215:     /*
        !           216:        Allow the MTR_PACKET environment variable to override
        !           217:        the path to the mtr-packet executable.  This is necessary
        !           218:        for debugging changes for mtr-packet.
        !           219:      */
        !           220:     char *mtr_packet_path = getenv("MTR_PACKET");
        !           221:     if (mtr_packet_path == NULL) {
        !           222:         mtr_packet_path = "mtr-packet";
        !           223:     }
        !           224: 
        !           225:     /*
        !           226:        First, try to execute mtr-packet from PATH
        !           227:        or MTR_PACKET environment variable.
        !           228:      */
        !           229:     execlp(mtr_packet_path, "mtr-packet", (char *) NULL);
        !           230: 
        !           231:     /*
        !           232:        If mtr-packet is not found, try to use mtr-packet from current directory
        !           233:      */
        !           234:     execl("./mtr-packet", "./mtr-packet", (char *) NULL);
        !           235: 
        !           236:     /*  Both exec attempts failed, so nothing to do but exit  */
        !           237:     exit(1);
        !           238: }
        !           239: 
        !           240: 
        !           241: /*  Create the command pipe to a new mtr-packet subprocess  */
        !           242: int open_command_pipe(
        !           243:     struct mtr_ctl *ctl,
        !           244:     struct packet_command_pipe_t *cmdpipe)
        !           245: {
        !           246:     int stdin_pipe[2];
        !           247:     int stdout_pipe[2];
        !           248:     pid_t child_pid;
        !           249:     int i;
        !           250: 
        !           251:     /*
        !           252:        We actually need two Unix pipes.  One for stdin and one for
        !           253:        stdout on the new process.
        !           254:      */
        !           255:     if (pipe(stdin_pipe) || pipe(stdout_pipe)) {
        !           256:         return errno;
        !           257:     }
        !           258: 
        !           259:     child_pid = fork();
        !           260:     if (child_pid == -1) {
        !           261:         return errno;
        !           262:     }
        !           263: 
        !           264:     if (child_pid == 0) {
        !           265:         /*
        !           266:            In the child process, attach our created pipes to stdin
        !           267:            and stdout
        !           268:          */
        !           269:         dup2(stdin_pipe[0], STDIN_FILENO);
        !           270:         dup2(stdout_pipe[1], STDOUT_FILENO);
        !           271: 
        !           272:         /*  Close all unnecessary fds  */
        !           273:         for (i = STDERR_FILENO + 1; i <= stdout_pipe[1]; i++) {
        !           274:             close(i);
        !           275:         }
        !           276: 
        !           277:         execute_packet_child();
        !           278:     } else {
        !           279:         memset(cmdpipe, 0, sizeof(struct packet_command_pipe_t));
        !           280: 
        !           281:         /*
        !           282:            In the parent process, save the opposite ends of the pipes
        !           283:            attached as stdin and stdout in the child.
        !           284:          */
        !           285:         cmdpipe->pid = child_pid;
        !           286:         cmdpipe->read_fd = stdout_pipe[0];
        !           287:         cmdpipe->write_fd = stdin_pipe[1];
        !           288: 
        !           289:         /*  We don't need the child ends of the pipe open in the parent.  */
        !           290:         close(stdout_pipe[1]);
        !           291:         close(stdin_pipe[0]);
        !           292: 
        !           293:         /*
        !           294:            Check that we can communicate with the client.  If we failed to
        !           295:            execute the mtr-packet binary, we will discover that here.
        !           296:          */
        !           297:         if (check_feature(ctl, cmdpipe, "send-probe")) {
        !           298:             error(EXIT_FAILURE, errno, "Failure to start mtr-packet");
        !           299:         }
        !           300: 
        !           301:         if (check_packet_features(ctl, cmdpipe)) {
        !           302:             error(EXIT_FAILURE, errno, "Packet type unsupported");
        !           303:         }
        !           304: 
        !           305:         /*  We will need non-blocking reads from the child  */
        !           306:         set_fd_nonblock(cmdpipe->read_fd);
        !           307:     }
        !           308: 
        !           309:     return 0;
        !           310: }
        !           311: 
        !           312: 
        !           313: /*  Kill the mtr-packet child process and close the command pipe  */
        !           314: void close_command_pipe(
        !           315:     struct packet_command_pipe_t *cmdpipe)
        !           316: {
        !           317:     int child_exit_value;
        !           318: 
        !           319:     if (cmdpipe->pid) {
        !           320:         close(cmdpipe->read_fd);
        !           321:         close(cmdpipe->write_fd);
        !           322: 
        !           323:         kill(cmdpipe->pid, SIGTERM);
        !           324:         waitpid(cmdpipe->pid, &child_exit_value, 0);
        !           325:     }
        !           326: 
        !           327:     memset(cmdpipe, 0, sizeof(struct packet_command_pipe_t));
        !           328: }
        !           329: 
        !           330: 
        !           331: /*  Start building the command string for the "send-probe" command  */
        !           332: static
        !           333: void construct_base_command(
        !           334:     struct mtr_ctl *ctl,
        !           335:     char *command,
        !           336:     int buffer_size,
        !           337:     int command_token,
        !           338:     ip_t * address,
        !           339:     ip_t * localaddress)
        !           340: {
        !           341:     char ip_string[INET6_ADDRSTRLEN];
        !           342:     char local_ip_string[INET6_ADDRSTRLEN];
        !           343:     const char *ip_type;
        !           344:     const char *local_ip_type;
        !           345:     const char *protocol = NULL;
        !           346: 
        !           347:     /*  Conver the remote IP address to a string  */
        !           348:     if (inet_ntop(ctl->af, address, ip_string, INET6_ADDRSTRLEN) == NULL) {
        !           349: 
        !           350:         display_close(ctl);
        !           351:         error(EXIT_FAILURE, errno, "invalid remote IP address");
        !           352:     }
        !           353: 
        !           354:     if (inet_ntop(ctl->af, localaddress,
        !           355:                   local_ip_string, INET6_ADDRSTRLEN) == NULL) {
        !           356: 
        !           357:         display_close(ctl);
        !           358:         error(EXIT_FAILURE, errno, "invalid local IP address");
        !           359:     }
        !           360: 
        !           361:     if (ctl->af == AF_INET6) {
        !           362:         ip_type = "ip-6";
        !           363:         local_ip_type = "local-ip-6";
        !           364:     } else {
        !           365:         ip_type = "ip-4";
        !           366:         local_ip_type = "local-ip-4";
        !           367:     }
        !           368: 
        !           369:     if (ctl->mtrtype == IPPROTO_ICMP) {
        !           370:         protocol = "icmp";
        !           371:     } else if (ctl->mtrtype == IPPROTO_UDP) {
        !           372:         protocol = "udp";
        !           373:     } else if (ctl->mtrtype == IPPROTO_TCP) {
        !           374:         protocol = "tcp";
        !           375: #ifdef HAS_SCTP
        !           376:     } else if (ctl->mtrtype == IPPROTO_SCTP) {
        !           377:         protocol = "sctp";
        !           378: #endif
        !           379:     } else {
        !           380:         display_close(ctl);
        !           381:         error(EXIT_FAILURE, 0,
        !           382:               "protocol unsupported by mtr-packet interface");
        !           383:     }
        !           384: 
        !           385:     snprintf(command, buffer_size,
        !           386:              "%d send-probe %s %s %s %s protocol %s",
        !           387:              command_token,
        !           388:              ip_type, ip_string, local_ip_type, local_ip_string, protocol);
        !           389: }
        !           390: 
        !           391: 
        !           392: /*  Append an argument to the "send-probe" command string  */
        !           393: static
        !           394: void append_command_argument(
        !           395:     char *command,
        !           396:     int buffer_size,
        !           397:     char *name,
        !           398:     int value)
        !           399: {
        !           400:     char argument[COMMAND_BUFFER_SIZE];
        !           401:     int remaining_size;
        !           402: 
        !           403:     remaining_size = buffer_size - strlen(command) - 1;
        !           404: 
        !           405:     snprintf(argument, buffer_size, " %s %d", name, value);
        !           406:     strncat(command, argument, remaining_size);
        !           407: }
        !           408: 
        !           409: 
        !           410: /*  Request a new probe from the "mtr-packet" child process  */
        !           411: void send_probe_command(
        !           412:     struct mtr_ctl *ctl,
        !           413:     struct packet_command_pipe_t *cmdpipe,
        !           414:     ip_t * address,
        !           415:     ip_t * localaddress,
        !           416:     int packet_size,
        !           417:     int sequence,
        !           418:     int time_to_live)
        !           419: {
        !           420:     char command[COMMAND_BUFFER_SIZE];
        !           421:     int remaining_size;
        !           422:     int timeout;
        !           423: 
        !           424:     construct_base_command(ctl, command, COMMAND_BUFFER_SIZE, sequence,
        !           425:                            address, localaddress);
        !           426: 
        !           427:     append_command_argument(command, COMMAND_BUFFER_SIZE, "size",
        !           428:                             packet_size);
        !           429: 
        !           430:     append_command_argument(command, COMMAND_BUFFER_SIZE, "bit-pattern",
        !           431:                             ctl->bitpattern);
        !           432: 
        !           433:     append_command_argument(command, COMMAND_BUFFER_SIZE, "tos", ctl->tos);
        !           434: 
        !           435:     append_command_argument(command, COMMAND_BUFFER_SIZE, "ttl",
        !           436:                             time_to_live);
        !           437: 
        !           438:     timeout = ctl->probe_timeout / 1000000;
        !           439:     append_command_argument(command, COMMAND_BUFFER_SIZE, "timeout",
        !           440:                             timeout);
        !           441: 
        !           442:     if (ctl->remoteport) {
        !           443:         append_command_argument(command, COMMAND_BUFFER_SIZE, "port",
        !           444:                                 ctl->remoteport);
        !           445:     }
        !           446: 
        !           447:     if (ctl->localport) {
        !           448:         append_command_argument(command, COMMAND_BUFFER_SIZE, "local-port",
        !           449:                                 ctl->localport);
        !           450:     }
        !           451: #ifdef SO_MARK
        !           452:     if (ctl->mark) {
        !           453:         append_command_argument(command, COMMAND_BUFFER_SIZE, "mark",
        !           454:                                 ctl->mark);
        !           455:     }
        !           456: #endif
        !           457: 
        !           458:     remaining_size = COMMAND_BUFFER_SIZE - strlen(command) - 1;
        !           459:     strncat(command, "\n", remaining_size);
        !           460: 
        !           461:     /*  Send a probe using the mtr-packet subprocess  */
        !           462:     if (write(cmdpipe->write_fd, command, strlen(command)) == -1) {
        !           463:         display_close(ctl);
        !           464:         error(EXIT_FAILURE, errno,
        !           465:               "mtr-packet command pipe write failure");
        !           466:     }
        !           467: }
        !           468: 
        !           469: 
        !           470: /*
        !           471:     Parse a comma separated field of mpls values, filling out the mplslen
        !           472:     structure with mpls labels.
        !           473: */
        !           474: static
        !           475: void parse_mpls_values(
        !           476:     struct mplslen *mpls,
        !           477:     char *value_str)
        !           478: {
        !           479:     char *next_value = value_str;
        !           480:     char *end_of_value;
        !           481:     int value;
        !           482:     int label_count = 0;
        !           483:     int label_field = 0;
        !           484: 
        !           485:     while (*next_value) {
        !           486:         value = strtol(next_value, &end_of_value, 10);
        !           487: 
        !           488:         /*  Failure to advance means an invalid numeric value  */
        !           489:         if (end_of_value == next_value) {
        !           490:             return;
        !           491:         }
        !           492: 
        !           493:         /*  If the next character is not a comma or a NUL, we have
        !           494:            an invalid string  */
        !           495:         if (*end_of_value == ',') {
        !           496:             next_value = end_of_value + 1;
        !           497:         } else if (*end_of_value == 0) {
        !           498:             next_value = end_of_value;
        !           499:         } else {
        !           500:             return;
        !           501:         }
        !           502: 
        !           503:         /*
        !           504:            Store the converted value in the next field of the MPLS
        !           505:            structure.
        !           506:          */
        !           507:         if (label_field == 0) {
        !           508:             mpls->label[label_count] = value;
        !           509:         } else if (label_field == 1) {
        !           510:             mpls->exp[label_count] = value;
        !           511:         } else if (label_field == 2) {
        !           512:             mpls->s[label_count] = value;
        !           513:         } else if (label_field == 3) {
        !           514:             mpls->ttl[label_count] = value;
        !           515:         }
        !           516: 
        !           517:         label_field++;
        !           518:         if (label_field > 3) {
        !           519:             label_field = 0;
        !           520:             label_count++;
        !           521:         }
        !           522: 
        !           523:         /*
        !           524:            If we've used up all MPLS labels in the structure, return with
        !           525:            what we've got
        !           526:          */
        !           527:         if (label_count >= MAXLABELS) {
        !           528:             break;
        !           529:         }
        !           530:     }
        !           531: 
        !           532:     mpls->labels = label_count;
        !           533: }
        !           534: 
        !           535: 
        !           536: /*
        !           537:     Extract the IP address and round trip time from a reply to a probe.
        !           538:     Returns true if both arguments are found in the reply, false otherwise.
        !           539: */
        !           540: static
        !           541: bool parse_reply_arguments(
        !           542:     struct mtr_ctl *ctl,
        !           543:     struct command_t *reply,
        !           544:     ip_t * fromaddress,
        !           545:     int *round_trip_time,
        !           546:     struct mplslen *mpls)
        !           547: {
        !           548:     bool found_round_trip;
        !           549:     bool found_ip;
        !           550:     char *arg_name;
        !           551:     char *arg_value;
        !           552:     int i;
        !           553: 
        !           554:     *round_trip_time = 0;
        !           555:     memset(fromaddress, 0, sizeof(ip_t));
        !           556:     memset(mpls, 0, sizeof(struct mplslen));
        !           557: 
        !           558:     found_ip = false;
        !           559:     found_round_trip = false;
        !           560: 
        !           561:     /*  Examine the reply arguments for known values  */
        !           562:     for (i = 0; i < reply->argument_count; i++) {
        !           563:         arg_name = reply->argument_name[i];
        !           564:         arg_value = reply->argument_value[i];
        !           565: 
        !           566:         if (ctl->af == AF_INET6) {
        !           567:             /*  IPv6 address of the responding host  */
        !           568:             if (!strcmp(arg_name, "ip-6")) {
        !           569:                 if (inet_pton(AF_INET6, arg_value, fromaddress)) {
        !           570:                     found_ip = true;
        !           571:                 }
        !           572:             }
        !           573:         } else {
        !           574:             /*  IPv4 address of the responding host  */
        !           575:             if (!strcmp(arg_name, "ip-4")) {
        !           576:                 if (inet_pton(AF_INET, arg_value, fromaddress)) {
        !           577:                     found_ip = true;
        !           578:                 }
        !           579:             }
        !           580:         }
        !           581: 
        !           582:         /*  The round trip time in microseconds  */
        !           583:         if (!strcmp(arg_name, "round-trip-time")) {
        !           584:             errno = 0;
        !           585:             *round_trip_time = strtol(arg_value, NULL, 10);
        !           586:             if (!errno) {
        !           587:                 found_round_trip = true;
        !           588:             }
        !           589:         }
        !           590: 
        !           591:         /*  MPLS labels  */
        !           592:         if (!strcmp(arg_name, "mpls")) {
        !           593:             parse_mpls_values(mpls, arg_value);
        !           594:         }
        !           595:     }
        !           596: 
        !           597:     return found_ip && found_round_trip;
        !           598: }
        !           599: 
        !           600: 
        !           601: /*
        !           602:     If an mtr-packet command has returned an error result,
        !           603:     report the error and exit.
        !           604: */
        !           605: static
        !           606: void handle_reply_errors(
        !           607:     struct mtr_ctl *ctl,
        !           608:     struct command_t *reply)
        !           609: {
        !           610:     char *reply_name;
        !           611: 
        !           612:     reply_name = reply->command_name;
        !           613: 
        !           614:     if (!strcmp(reply_name, "no-route")) {
        !           615:         display_close(ctl);
        !           616:         error(EXIT_FAILURE, 0, "No route to host");
        !           617:     }
        !           618: 
        !           619:     if (!strcmp(reply_name, "network-down")) {
        !           620:         display_close(ctl);
        !           621:         error(EXIT_FAILURE, 0, "Network down");
        !           622:     }
        !           623: 
        !           624:     if (!strcmp(reply_name, "probes-exhausted")) {
        !           625:         display_close(ctl);
        !           626:         error(EXIT_FAILURE, 0, "Probes exhausted");
        !           627:     }
        !           628: 
        !           629:     if (!strcmp(reply_name, "invalid-argument")) {
        !           630:         display_close(ctl);
        !           631:         error(EXIT_FAILURE, 0, "mtr-packet reported invalid argument");
        !           632:     }
        !           633: 
        !           634:     if (!strcmp(reply_name, "permission-denied")) {
        !           635:         display_close(ctl);
        !           636:         error(EXIT_FAILURE, 0, "Permission denied");
        !           637:     }
        !           638: 
        !           639:     if (!strcmp(reply_name, "address-in-use")) {
        !           640:         display_close(ctl);
        !           641:         error(EXIT_FAILURE, 0, "Address in use");
        !           642:     }
        !           643: 
        !           644:     if (!strcmp(reply_name, "address-not-available")) {
        !           645:         display_close(ctl);
        !           646:         error(EXIT_FAILURE, 0, "Address not available");
        !           647:     }
        !           648: 
        !           649:     if (!strcmp(reply_name, "unexpected-error")) {
        !           650:         display_close(ctl);
        !           651:         error(EXIT_FAILURE, 0, "Unexpected mtr-packet error");
        !           652:     }
        !           653: }
        !           654: 
        !           655: 
        !           656: /*
        !           657:     A complete mtr-packet reply line has arrived.  Parse it and record
        !           658:     the responding IP and round trip time, if it is a reply that we
        !           659:     understand.
        !           660: */
        !           661: static
        !           662: void handle_command_reply(
        !           663:     struct mtr_ctl *ctl,
        !           664:     char *reply_str,
        !           665:     probe_reply_func_t reply_func)
        !           666: {
        !           667:     struct command_t reply;
        !           668:     ip_t fromaddress;
        !           669:     int seq_num;
        !           670:     int round_trip_time;
        !           671:     char *reply_name;
        !           672:     struct mplslen mpls;
        !           673: 
        !           674:     /*  Parse the reply string  */
        !           675:     if (parse_command(&reply, reply_str)) {
        !           676:         /*
        !           677:            If the reply isn't well structured, something is fundamentally
        !           678:            wrong, as we might as well exit.  Even if the reply is of an
        !           679:            unknown type, it should still parse.
        !           680:          */
        !           681:         display_close(ctl);
        !           682:         error(EXIT_FAILURE, errno, "reply parse failure");
        !           683:         return;
        !           684:     }
        !           685: 
        !           686:     handle_reply_errors(ctl, &reply);
        !           687: 
        !           688:     seq_num = reply.token;
        !           689:     reply_name = reply.command_name;
        !           690: 
        !           691:     /*  If the reply type is unknown, ignore it for future compatibility  */
        !           692:     if (strcmp(reply_name, "reply") && strcmp(reply_name, "ttl-expired")) {
        !           693:         return;
        !           694:     }
        !           695: 
        !           696:     /*
        !           697:        If the reply had an IP address and a round trip time, we can
        !           698:        record the result.
        !           699:      */
        !           700:     if (parse_reply_arguments
        !           701:         (ctl, &reply, &fromaddress, &round_trip_time, &mpls)) {
        !           702: 
        !           703:         reply_func(ctl, seq_num, &mpls, (void *) &fromaddress,
        !           704:                    round_trip_time);
        !           705:     }
        !           706: }
        !           707: 
        !           708: 
        !           709: /*
        !           710:     Check the command pipe for completed replies to commands
        !           711:     we have previously sent.  Record the results of those replies.
        !           712: */
        !           713: static
        !           714: void consume_reply_buffer(
        !           715:     struct mtr_ctl *ctl,
        !           716:     struct packet_command_pipe_t *cmdpipe,
        !           717:     probe_reply_func_t reply_func)
        !           718: {
        !           719:     char *reply_buffer;
        !           720:     char *reply_start;
        !           721:     char *end_of_reply;
        !           722:     int used_size;
        !           723:     int move_size;
        !           724: 
        !           725:     reply_buffer = cmdpipe->reply_buffer;
        !           726: 
        !           727:     /*  Terminate the string storing the replies  */
        !           728:     assert(cmdpipe->reply_buffer_used < PACKET_REPLY_BUFFER_SIZE);
        !           729:     reply_buffer[cmdpipe->reply_buffer_used] = 0;
        !           730: 
        !           731:     reply_start = reply_buffer;
        !           732: 
        !           733:     /*
        !           734:        We may have multiple completed replies.  Loop until we don't
        !           735:        have any more newlines termininating replies.
        !           736:      */
        !           737:     while (true) {
        !           738:         /*  If no newline is found, our reply isn't yet complete  */
        !           739:         end_of_reply = index(reply_start, '\n');
        !           740:         if (end_of_reply == NULL) {
        !           741:             /*  No complete replies remaining  */
        !           742:             break;
        !           743:         }
        !           744: 
        !           745:         /*
        !           746:            Terminate the reply string at the newline, which
        !           747:            is necessary in the case where we are able to read
        !           748:            mulitple replies arriving simultaneously.
        !           749:          */
        !           750:         *end_of_reply = 0;
        !           751: 
        !           752:         /*  Parse and record the reply results  */
        !           753:         handle_command_reply(ctl, reply_start, reply_func);
        !           754: 
        !           755:         reply_start = end_of_reply + 1;
        !           756:     }
        !           757: 
        !           758:     /*
        !           759:        After replies have been processed, free the space used
        !           760:        by the replies, and move any remaining partial reply text
        !           761:        to the start of the reply buffer.
        !           762:      */
        !           763:     used_size = reply_start - reply_buffer;
        !           764:     move_size = cmdpipe->reply_buffer_used - used_size;
        !           765:     memmove(reply_buffer, reply_start, move_size);
        !           766:     cmdpipe->reply_buffer_used -= used_size;
        !           767: 
        !           768:     if (cmdpipe->reply_buffer_used >= PACKET_REPLY_BUFFER_SIZE - 1) {
        !           769:         /*
        !           770:            We've overflowed the reply buffer without a complete reply.
        !           771:            There's not much we can do about it but discard the data
        !           772:            we've got and hope new data coming in fits.
        !           773:          */
        !           774:         cmdpipe->reply_buffer_used = 0;
        !           775:     }
        !           776: }
        !           777: 
        !           778: 
        !           779: /*
        !           780:     Read as much as we can from the reply pipe from the child process, and
        !           781:     process as many replies as are available.
        !           782: */
        !           783: void handle_command_replies(
        !           784:     struct mtr_ctl *ctl,
        !           785:     struct packet_command_pipe_t *cmdpipe,
        !           786:     probe_reply_func_t reply_func)
        !           787: {
        !           788:     int read_count;
        !           789:     int buffer_remaining;
        !           790:     char *reply_buffer;
        !           791:     char *read_buffer;
        !           792: 
        !           793:     reply_buffer = cmdpipe->reply_buffer;
        !           794: 
        !           795:     /*
        !           796:        Read the available reply text, up to the the remaining
        !           797:        buffer space.  (Minus one for the terminating NUL.)
        !           798:      */
        !           799:     read_buffer = &reply_buffer[cmdpipe->reply_buffer_used];
        !           800:     buffer_remaining =
        !           801:         PACKET_REPLY_BUFFER_SIZE - cmdpipe->reply_buffer_used;
        !           802:     read_count = read(cmdpipe->read_fd, read_buffer, buffer_remaining - 1);
        !           803: 
        !           804:     if (read_count < 0) {
        !           805:         /*
        !           806:            EAGAIN simply indicates that there is no data currently
        !           807:            available on our non-blocking pipe.
        !           808:          */
        !           809:         if (errno == EAGAIN) {
        !           810:             return;
        !           811:         }
        !           812: 
        !           813:         display_close(ctl);
        !           814:         error(EXIT_FAILURE, errno, "command reply read failure");
        !           815:         return;
        !           816:     }
        !           817: 
        !           818:     if (read_count == 0) {
        !           819:         display_close(ctl);
        !           820: 
        !           821:         errno = EPIPE;
        !           822:         error(EXIT_FAILURE, EPIPE, "unexpected packet generator exit");
        !           823:     }
        !           824: 
        !           825:     cmdpipe->reply_buffer_used += read_count;
        !           826: 
        !           827:     /*  Handle any replies completed by this read  */
        !           828:     consume_reply_buffer(ctl, cmdpipe, reply_func);
        !           829: }

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