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