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