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