Annotation of embedaddon/mtr/ui/cmdpipe.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 "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>