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