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