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