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