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