1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1982, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #include <sys/param.h> 34 #include <sys/protosw.h> 35 #include <sys/socket.h> 36 #include <sys/time.h> 37 #include <netinet/in.h> 38 #define RIPVERSION RIPv2 39 #include <protocols/routed.h> 40 #include <arpa/inet.h> 41 #include <netdb.h> 42 #include <errno.h> 43 #include <unistd.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #ifdef sgi 48 #include <strings.h> 49 #include <bstring.h> 50 #endif 51 52 #define UNUSED __attribute__((unused)) 53 #ifndef sgi 54 #define _HAVE_SIN_LEN 55 #endif 56 57 #ifdef __NetBSD__ 58 #include <md5.h> 59 #else 60 #define MD5_DIGEST_LEN 16 61 typedef struct { 62 u_int32_t state[4]; /* state (ABCD) */ 63 u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */ 64 unsigned char buffer[64]; /* input buffer */ 65 } MD5_CTX; 66 extern void MD5Init(MD5_CTX*); 67 extern void MD5Update(MD5_CTX*, u_char*, u_int); 68 extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*); 69 #endif 70 71 72 #define WTIME 15 /* Time to wait for all responses */ 73 #define STIME (250*1000) /* usec to wait for another response */ 74 75 int soc; 76 77 const char *pgmname; 78 79 union { 80 struct rip rip; 81 char packet[MAXPACKETSIZE+MAXPATHLEN]; 82 } omsg_buf; 83 #define OMSG omsg_buf.rip 84 int omsg_len = sizeof(struct rip); 85 86 union { 87 struct rip rip; 88 char packet[MAXPACKETSIZE+1024]; 89 } imsg_buf; 90 #define IMSG imsg_buf.rip 91 92 int nflag; /* numbers, no names */ 93 int pflag; /* play the `gated` game */ 94 int ripv2 = 1; /* use RIP version 2 */ 95 int wtime = WTIME; 96 int rflag; /* 1=ask about a particular route */ 97 int trace, not_trace; /* send trace command or not */ 98 int auth_type = RIP_AUTH_NONE; 99 char passwd[RIP_AUTH_PW_LEN]; 100 u_long keyid; 101 102 struct timeval sent; /* when query sent */ 103 104 static char localhost_str[] = "localhost"; 105 static char *default_argv[] = {localhost_str, 0}; 106 107 static void rip_input(struct sockaddr_in*, int); 108 static int out(const char *); 109 static void trace_loop(char *argv[]) __attribute((__noreturn__)); 110 static void query_loop(char *argv[], int) __attribute((__noreturn__)); 111 static int getnet(char *, struct netinfo *); 112 static u_int std_mask(u_int); 113 static int parse_quote(char **, const char *, char *, char *, int); 114 static void usage(void) __dead2; 115 116 117 int 118 main(int argc, 119 char *argv[]) 120 { 121 int ch, bsize; 122 char *p, *options, *value, delim; 123 const char *result; 124 125 OMSG.rip_nets[0].n_dst = RIP_DEFAULT; 126 OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC; 127 OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); 128 129 pgmname = argv[0]; 130 while ((ch = getopt(argc, argv, "np1w:r:t:a:")) != -1) 131 switch (ch) { 132 case 'n': 133 not_trace = 1; 134 nflag = 1; 135 break; 136 137 case 'p': 138 not_trace = 1; 139 pflag = 1; 140 break; 141 142 case '1': 143 ripv2 = 0; 144 break; 145 146 case 'w': 147 not_trace = 1; 148 wtime = (int)strtoul(optarg, &p, 0); 149 if (*p != '\0' 150 || wtime <= 0) 151 usage(); 152 break; 153 154 case 'r': 155 not_trace = 1; 156 if (rflag) 157 usage(); 158 rflag = getnet(optarg, &OMSG.rip_nets[0]); 159 if (!rflag) { 160 struct hostent *hp = gethostbyname(optarg); 161 if (hp == NULL) { 162 fprintf(stderr, "%s: %s:", 163 pgmname, optarg); 164 herror(0); 165 exit(1); 166 } 167 memcpy(&OMSG.rip_nets[0].n_dst, hp->h_addr, 168 sizeof(OMSG.rip_nets[0].n_dst)); 169 OMSG.rip_nets[0].n_family = RIP_AF_INET; 170 OMSG.rip_nets[0].n_mask = -1; 171 rflag = 1; 172 } 173 break; 174 175 case 't': 176 trace = 1; 177 options = optarg; 178 while (*options != '\0') { 179 /* messy complications to make -W -Wall happy */ 180 static char on_str[] = "on"; 181 static char more_str[] = "more"; 182 static char off_str[] = "off"; 183 static char dump_str[] = "dump"; 184 static char *traceopts[] = { 185 # define TRACE_ON 0 186 on_str, 187 # define TRACE_MORE 1 188 more_str, 189 # define TRACE_OFF 2 190 off_str, 191 # define TRACE_DUMP 3 192 dump_str, 193 0 194 }; 195 result = ""; 196 switch (getsubopt(&options,traceopts,&value)) { 197 case TRACE_ON: 198 OMSG.rip_cmd = RIPCMD_TRACEON; 199 if (!value 200 || strlen(value) > MAXPATHLEN) 201 usage(); 202 result = value; 203 break; 204 case TRACE_MORE: 205 if (value) 206 usage(); 207 OMSG.rip_cmd = RIPCMD_TRACEON; 208 break; 209 case TRACE_OFF: 210 if (value) 211 usage(); 212 OMSG.rip_cmd = RIPCMD_TRACEOFF; 213 break; 214 case TRACE_DUMP: 215 if (value) 216 usage(); 217 OMSG.rip_cmd = RIPCMD_TRACEON; 218 result = "dump/../table"; 219 break; 220 default: 221 usage(); 222 } 223 strcpy((char*)OMSG.rip_tracefile, result); 224 omsg_len += strlen(result) - sizeof(OMSG.ripun); 225 } 226 break; 227 228 case 'a': 229 not_trace = 1; 230 p = strchr(optarg,'='); 231 if (!p) 232 usage(); 233 *p++ = '\0'; 234 if (!strcasecmp("passwd",optarg)) 235 auth_type = RIP_AUTH_PW; 236 else if (!strcasecmp("md5_passwd",optarg)) 237 auth_type = RIP_AUTH_MD5; 238 else 239 usage(); 240 if (0 > parse_quote(&p,"|",&delim, 241 passwd, sizeof(passwd))) 242 usage(); 243 if (auth_type == RIP_AUTH_MD5 244 && delim == '|') { 245 keyid = strtoul(p+1,&p,0); 246 if (keyid > 255 || *p != '\0') 247 usage(); 248 } else if (delim != '\0') { 249 usage(); 250 } 251 break; 252 253 default: 254 usage(); 255 } 256 argv += optind; 257 argc -= optind; 258 if (not_trace && trace) 259 usage(); 260 if (argc == 0) { 261 argc = 1; 262 argv = default_argv; 263 } 264 265 soc = socket(AF_INET, SOCK_DGRAM, 0); 266 if (soc < 0) { 267 perror("socket"); 268 exit(2); 269 } 270 271 /* be prepared to receive a lot of routes */ 272 for (bsize = 127*1024; ; bsize -= 1024) { 273 if (setsockopt(soc, SOL_SOCKET, SO_RCVBUF, 274 &bsize, sizeof(bsize)) == 0) 275 break; 276 if (bsize <= 4*1024) { 277 perror("setsockopt SO_RCVBUF"); 278 break; 279 } 280 } 281 282 if (trace) 283 trace_loop(argv); 284 else 285 query_loop(argv, argc); 286 /* NOTREACHED */ 287 return 0; 288 } 289 290 291 static void 292 usage(void) 293 { 294 fprintf(stderr, 295 "usage: rtquery [-np1] [-r tgt_rt] [-w wtime]" 296 " [-a type=passwd] host1 [host2 ...]\n" 297 "\trtquery -t {on=filename|more|off|dump}" 298 " host1 [host2 ...]\n"); 299 exit(1); 300 } 301 302 303 /* tell the target hosts about tracing 304 */ 305 static void 306 trace_loop(char *argv[]) 307 { 308 struct sockaddr_in myaddr; 309 int res; 310 311 if (geteuid() != 0) { 312 (void)fprintf(stderr, "-t requires UID 0\n"); 313 exit(1); 314 } 315 316 if (ripv2) { 317 OMSG.rip_vers = RIPv2; 318 } else { 319 OMSG.rip_vers = RIPv1; 320 } 321 322 memset(&myaddr, 0, sizeof(myaddr)); 323 myaddr.sin_family = AF_INET; 324 #ifdef _HAVE_SIN_LEN 325 myaddr.sin_len = sizeof(myaddr); 326 #endif 327 myaddr.sin_port = htons(IPPORT_RESERVED-1); 328 while (bind(soc, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { 329 if (errno != EADDRINUSE 330 || myaddr.sin_port == 0) { 331 perror("bind"); 332 exit(2); 333 } 334 myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1); 335 } 336 337 res = 1; 338 while (*argv != NULL) { 339 if (out(*argv++) <= 0) 340 res = 0; 341 } 342 exit(res); 343 } 344 345 346 /* query all of the listed hosts 347 */ 348 static void 349 query_loop(char *argv[], int argc) 350 { 351 # define NA0 (OMSG.rip_auths[0]) 352 # define NA2 (OMSG.rip_auths[2]) 353 struct seen { 354 struct seen *next; 355 struct in_addr addr; 356 } *seen, *sp; 357 int answered = 0; 358 int cc; 359 fd_set bits; 360 struct timeval now, delay; 361 struct sockaddr_in from; 362 int fromlen; 363 MD5_CTX md5_ctx; 364 365 366 OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST; 367 if (ripv2) { 368 OMSG.rip_vers = RIPv2; 369 if (auth_type == RIP_AUTH_PW) { 370 OMSG.rip_nets[1] = OMSG.rip_nets[0]; 371 NA0.a_family = RIP_AF_AUTH; 372 NA0.a_type = RIP_AUTH_PW; 373 memcpy(NA0.au.au_pw, passwd, RIP_AUTH_PW_LEN); 374 omsg_len += sizeof(OMSG.rip_nets[0]); 375 376 } else if (auth_type == RIP_AUTH_MD5) { 377 OMSG.rip_nets[1] = OMSG.rip_nets[0]; 378 NA0.a_family = RIP_AF_AUTH; 379 NA0.a_type = RIP_AUTH_MD5; 380 NA0.au.a_md5.md5_keyid = (int8_t)keyid; 381 NA0.au.a_md5.md5_auth_len = RIP_AUTH_MD5_KEY_LEN; 382 NA0.au.a_md5.md5_seqno = 0; 383 cc = (char *)&NA2-(char *)&OMSG; 384 NA0.au.a_md5.md5_pkt_len = htons(cc); 385 NA2.a_family = RIP_AF_AUTH; 386 NA2.a_type = htons(1); 387 MD5Init(&md5_ctx); 388 MD5Update(&md5_ctx, 389 (u_char *)&OMSG, cc); 390 MD5Update(&md5_ctx, 391 (u_char *)passwd, RIP_AUTH_MD5_HASH_LEN); 392 MD5Final(NA2.au.au_pw, &md5_ctx); 393 omsg_len += 2*sizeof(OMSG.rip_nets[0]); 394 } 395 396 } else { 397 OMSG.rip_vers = RIPv1; 398 OMSG.rip_nets[0].n_mask = 0; 399 } 400 401 /* ask the first (valid) host */ 402 seen = NULL; 403 while (0 > out(*argv++)) { 404 if (*argv == NULL) 405 exit(1); 406 answered++; 407 } 408 409 FD_ZERO(&bits); 410 for (;;) { 411 FD_SET(soc, &bits); 412 delay.tv_sec = 0; 413 delay.tv_usec = STIME; 414 cc = select(soc+1, &bits, 0,0, &delay); 415 if (cc > 0) { 416 fromlen = sizeof(from); 417 cc = recvfrom(soc, imsg_buf.packet, 418 sizeof(imsg_buf.packet), 0, 419 (struct sockaddr *)&from, &fromlen); 420 if (cc < 0) { 421 perror("recvfrom"); 422 exit(1); 423 } 424 /* count the distinct responding hosts. 425 * You cannot match responding hosts with 426 * addresses to which queries were transmitted, 427 * because a router might respond with a 428 * different source address. 429 */ 430 for (sp = seen; sp != NULL; sp = sp->next) { 431 if (sp->addr.s_addr == from.sin_addr.s_addr) 432 break; 433 } 434 if (sp == NULL) { 435 sp = malloc(sizeof(*sp)); 436 if (sp == NULL) { 437 fprintf(stderr, 438 "rtquery: malloc failed\n"); 439 exit(1); 440 } 441 sp->addr = from.sin_addr; 442 sp->next = seen; 443 seen = sp; 444 answered++; 445 } 446 447 rip_input(&from, cc); 448 continue; 449 } 450 451 if (cc < 0) { 452 if (errno == EINTR) 453 continue; 454 perror("select"); 455 exit(1); 456 } 457 458 /* After a pause in responses, probe another host. 459 * This reduces the intermingling of answers. 460 */ 461 while (*argv != NULL && out(*argv++) < 0) 462 answered++; 463 464 /* continue until no more packets arrive 465 * or we have heard from all hosts 466 */ 467 if (answered >= argc) 468 break; 469 470 /* or until we have waited a long time 471 */ 472 if (gettimeofday(&now, 0) < 0) { 473 perror("gettimeofday(now)"); 474 exit(1); 475 } 476 if (sent.tv_sec + wtime <= now.tv_sec) 477 break; 478 } 479 480 /* fail if there was no answer */ 481 exit (answered >= argc ? 0 : 1); 482 } 483 484 485 /* send to one host 486 */ 487 static int 488 out(const char *host) 489 { 490 struct sockaddr_in router; 491 struct hostent *hp; 492 493 if (gettimeofday(&sent, 0) < 0) { 494 perror("gettimeofday(sent)"); 495 return -1; 496 } 497 498 memset(&router, 0, sizeof(router)); 499 router.sin_family = AF_INET; 500 #ifdef _HAVE_SIN_LEN 501 router.sin_len = sizeof(router); 502 #endif 503 if (!inet_aton(host, &router.sin_addr)) { 504 hp = gethostbyname(host); 505 if (hp == NULL) { 506 herror(host); 507 return -1; 508 } 509 memcpy(&router.sin_addr, hp->h_addr, sizeof(router.sin_addr)); 510 } 511 router.sin_port = htons(RIP_PORT); 512 513 if (sendto(soc, &omsg_buf, omsg_len, 0, 514 (struct sockaddr *)&router, sizeof(router)) < 0) { 515 perror(host); 516 return -1; 517 } 518 519 return 0; 520 } 521 522 523 /* 524 * Convert string to printable characters 525 */ 526 static char * 527 qstring(u_char *s, int len) 528 { 529 static char buf[8*20+1]; 530 char *p; 531 u_char *s2, c; 532 533 534 for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) { 535 c = *s++; 536 if (c == '\0') { 537 for (s2 = s+1; s2 < &s[len]; s2++) { 538 if (*s2 != '\0') 539 break; 540 } 541 if (s2 >= &s[len]) 542 goto exit; 543 } 544 545 if (c >= ' ' && c < 0x7f && c != '\\') { 546 *p++ = c; 547 continue; 548 } 549 *p++ = '\\'; 550 switch (c) { 551 case '\\': 552 *p++ = '\\'; 553 break; 554 case '\n': 555 *p++= 'n'; 556 break; 557 case '\r': 558 *p++= 'r'; 559 break; 560 case '\t': 561 *p++ = 't'; 562 break; 563 case '\b': 564 *p++ = 'b'; 565 break; 566 default: 567 p += sprintf(p,"%o",c); 568 break; 569 } 570 } 571 exit: 572 *p = '\0'; 573 return buf; 574 } 575 576 577 /* 578 * Handle an incoming RIP packet. 579 */ 580 static void 581 rip_input(struct sockaddr_in *from, 582 int size) 583 { 584 struct netinfo *n, *lim; 585 struct in_addr in; 586 const char *name; 587 char net_buf[80]; 588 u_char hash[RIP_AUTH_MD5_KEY_LEN]; 589 MD5_CTX md5_ctx; 590 u_char md5_authed = 0; 591 u_int mask, dmask; 592 char *sp; 593 int i; 594 struct hostent *hp; 595 struct netent *np; 596 struct netauth *na; 597 598 599 if (nflag) { 600 printf("%s:", inet_ntoa(from->sin_addr)); 601 } else { 602 hp = gethostbyaddr((char*)&from->sin_addr, 603 sizeof(struct in_addr), AF_INET); 604 if (hp == NULL) { 605 printf("%s:", 606 inet_ntoa(from->sin_addr)); 607 } else { 608 printf("%s (%s):", hp->h_name, 609 inet_ntoa(from->sin_addr)); 610 } 611 } 612 if (IMSG.rip_cmd != RIPCMD_RESPONSE) { 613 printf("\n unexpected response type %d\n", IMSG.rip_cmd); 614 return; 615 } 616 printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers, 617 (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "", 618 size); 619 if (size > MAXPACKETSIZE) { 620 if (size > (int)sizeof(imsg_buf) - (int)sizeof(*n)) { 621 printf(" at least %d bytes too long\n", 622 size-MAXPACKETSIZE); 623 size = (int)sizeof(imsg_buf) - (int)sizeof(*n); 624 } else { 625 printf(" %d bytes too long\n", 626 size-MAXPACKETSIZE); 627 } 628 } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { 629 printf(" response of bad length=%d\n", size); 630 } 631 632 n = IMSG.rip_nets; 633 lim = (struct netinfo *)((char*)n + size) - 1; 634 for (; n <= lim; n++) { 635 name = ""; 636 if (n->n_family == RIP_AF_INET) { 637 in.s_addr = n->n_dst; 638 (void)strcpy(net_buf, inet_ntoa(in)); 639 640 mask = ntohl(n->n_mask); 641 dmask = mask & -mask; 642 if (mask != 0) { 643 sp = &net_buf[strlen(net_buf)]; 644 if (IMSG.rip_vers == RIPv1) { 645 (void)sprintf(sp," mask=%#x ? ",mask); 646 mask = 0; 647 } else if (mask + dmask == 0) { 648 for (i = 0; 649 (i != 32 650 && ((1<<i)&mask) == 0); 651 i++) 652 continue; 653 (void)sprintf(sp, "/%d",32-i); 654 } else { 655 (void)sprintf(sp," (mask %#x)", mask); 656 } 657 } 658 659 if (!nflag) { 660 if (mask == 0) { 661 mask = std_mask(in.s_addr); 662 if ((ntohl(in.s_addr) & ~mask) != 0) 663 mask = 0; 664 } 665 /* Without a netmask, do not worry about 666 * whether the destination is a host or a 667 * network. Try both and use the first name 668 * we get. 669 * 670 * If we have a netmask we can make a 671 * good guess. 672 */ 673 if ((in.s_addr & ~mask) == 0) { 674 np = getnetbyaddr((long)in.s_addr, 675 AF_INET); 676 if (np != NULL) 677 name = np->n_name; 678 else if (in.s_addr == 0) 679 name = "default"; 680 } 681 if (name[0] == '\0' 682 && ((in.s_addr & ~mask) != 0 683 || mask == 0xffffffff)) { 684 hp = gethostbyaddr((char*)&in, 685 sizeof(in), 686 AF_INET); 687 if (hp != NULL) 688 name = hp->h_name; 689 } 690 } 691 692 } else if (n->n_family == RIP_AF_AUTH) { 693 na = (struct netauth*)n; 694 if (na->a_type == RIP_AUTH_PW 695 && n == IMSG.rip_nets) { 696 (void)printf(" Password Authentication:" 697 " \"%s\"\n", 698 qstring(na->au.au_pw, 699 RIP_AUTH_PW_LEN)); 700 continue; 701 } 702 703 if (na->a_type == RIP_AUTH_MD5 704 && n == IMSG.rip_nets) { 705 (void)printf(" MD5 Auth" 706 " len=%d KeyID=%d" 707 " auth_len=%d" 708 " seqno=%#x" 709 " rsvd=%#x,%#x\n", 710 ntohs(na->au.a_md5.md5_pkt_len), 711 na->au.a_md5.md5_keyid, 712 na->au.a_md5.md5_auth_len, 713 (int)ntohl(na->au.a_md5.md5_seqno), 714 na->au.a_md5.rsvd[0], 715 na->au.a_md5.rsvd[1]); 716 md5_authed = 1; 717 continue; 718 } 719 (void)printf(" Authentication type %d: ", 720 ntohs(na->a_type)); 721 for (i = 0; i < (int)sizeof(na->au.au_pw); i++) 722 (void)printf("%02x ", na->au.au_pw[i]); 723 putc('\n', stdout); 724 if (md5_authed && n+1 > lim 725 && na->a_type == ntohs(1)) { 726 MD5Init(&md5_ctx); 727 MD5Update(&md5_ctx, (u_char *)&IMSG, 728 (char *)na-(char *)&IMSG 729 +RIP_AUTH_MD5_HASH_XTRA); 730 MD5Update(&md5_ctx, (u_char *)passwd, 731 RIP_AUTH_MD5_KEY_LEN); 732 MD5Final(hash, &md5_ctx); 733 (void)printf(" %s hash\n", 734 memcmp(hash, na->au.au_pw, 735 sizeof(hash)) 736 ? "WRONG" : "correct"); 737 } 738 continue; 739 740 } else { 741 (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d", 742 ntohs(n->n_family), 743 (u_char)(n->n_dst >> 24), 744 (u_char)(n->n_dst >> 16), 745 (u_char)(n->n_dst >> 8), 746 (u_char)n->n_dst); 747 } 748 749 (void)printf(" %-18s metric %2d %-10s", 750 net_buf, (int)ntohl(n->n_metric), name); 751 752 if (n->n_nhop != 0) { 753 in.s_addr = n->n_nhop; 754 if (nflag) 755 hp = NULL; 756 else 757 hp = gethostbyaddr((char*)&in, sizeof(in), 758 AF_INET); 759 (void)printf(" nhop=%-15s%s", 760 (hp != NULL) ? hp->h_name : inet_ntoa(in), 761 (IMSG.rip_vers == RIPv1) ? " ?" : ""); 762 } 763 if (n->n_tag != 0) 764 (void)printf(" tag=%#x%s", n->n_tag, 765 (IMSG.rip_vers == RIPv1) ? " ?" : ""); 766 putc('\n', stdout); 767 } 768 } 769 770 771 /* Return the classical netmask for an IP address. 772 */ 773 static u_int 774 std_mask(u_int addr) /* in network order */ 775 { 776 addr = ntohl(addr); /* was a host, not a network */ 777 778 if (addr == 0) /* default route has mask 0 */ 779 return 0; 780 if (IN_CLASSA(addr)) 781 return IN_CLASSA_NET; 782 if (IN_CLASSB(addr)) 783 return IN_CLASSB_NET; 784 return IN_CLASSC_NET; 785 } 786 787 788 /* get a network number as a name or a number, with an optional "/xx" 789 * netmask. 790 */ 791 static int /* 0=bad */ 792 getnet(char *name, 793 struct netinfo *rt) 794 { 795 int i; 796 struct netent *nentp; 797 u_int mask; 798 struct in_addr in; 799 char hname[MAXHOSTNAMELEN+1]; 800 char *mname, *p; 801 802 803 /* Detect and separate "1.2.3.4/24" 804 */ 805 if (NULL != (mname = strrchr(name,'/'))) { 806 i = (int)(mname - name); 807 if (i > (int)sizeof(hname)-1) /* name too long */ 808 return 0; 809 memmove(hname, name, i); 810 hname[i] = '\0'; 811 mname++; 812 name = hname; 813 } 814 815 nentp = getnetbyname(name); 816 if (nentp != NULL) { 817 in.s_addr = nentp->n_net; 818 } else if (inet_aton(name, &in) == 1) { 819 in.s_addr = ntohl(in.s_addr); 820 } else { 821 return 0; 822 } 823 824 if (mname == NULL) { 825 mask = std_mask(in.s_addr); 826 if ((~mask & in.s_addr) != 0) 827 mask = 0xffffffff; 828 } else { 829 mask = (u_int)strtoul(mname, &p, 0); 830 if (*p != '\0' || mask > 32) 831 return 0; 832 mask = 0xffffffff << (32-mask); 833 } 834 835 rt->n_dst = htonl(in.s_addr); 836 rt->n_family = RIP_AF_INET; 837 rt->n_mask = htonl(mask); 838 return 1; 839 } 840 841 842 /* strtok(), but honoring backslash 843 */ 844 static int /* -1=bad */ 845 parse_quote(char **linep, 846 const char *delims, 847 char *delimp, 848 char *buf, 849 int lim) 850 { 851 char c, *pc; 852 const char *p; 853 854 855 pc = *linep; 856 if (*pc == '\0') 857 return -1; 858 859 for (;;) { 860 if (lim == 0) 861 return -1; 862 c = *pc++; 863 if (c == '\0') 864 break; 865 866 if (c == '\\' && *pc != '\0') { 867 if ((c = *pc++) == 'n') { 868 c = '\n'; 869 } else if (c == 'r') { 870 c = '\r'; 871 } else if (c == 't') { 872 c = '\t'; 873 } else if (c == 'b') { 874 c = '\b'; 875 } else if (c >= '0' && c <= '7') { 876 c -= '0'; 877 if (*pc >= '0' && *pc <= '7') { 878 c = (c<<3)+(*pc++ - '0'); 879 if (*pc >= '0' && *pc <= '7') 880 c = (c<<3)+(*pc++ - '0'); 881 } 882 } 883 884 } else { 885 for (p = delims; *p != '\0'; ++p) { 886 if (*p == c) 887 goto exit; 888 } 889 } 890 891 *buf++ = c; 892 --lim; 893 } 894 exit: 895 if (delimp != NULL) 896 *delimp = c; 897 *linep = pc-1; 898 if (lim != 0) 899 *buf = '\0'; 900 return 0; 901 } 902