Annotation of embedaddon/mtr/ui/cmdpipe.c, revision 1.1.1.2

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

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