xref: /netbsd-src/sbin/routed/rtquery/rtquery.c (revision 5aefcfdc06931dd97e76246d2fe0302f7b3fe094)
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