cmds.c revision 1.12 1 /* $NetBSD: cmds.c,v 1.12 2002/09/19 00:01:33 mycroft Exp $ */
2
3 /*-
4 * Copyright (c) 1985, 1993 The Regents of the University of California.
5 * 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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 3/26/95";
40 #else
41 __RCSID("$NetBSD: cmds.c,v 1.12 2002/09/19 00:01:33 mycroft Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include "timedc.h"
46 #include <sys/file.h>
47
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #include <netinet/ip_icmp.h>
51
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55
56 #define TSPTYPES
57 #include <protocols/timed.h>
58
59 #define SECHR (60*60)
60 #define SECDAY (24*SECHR)
61
62 # define DATE_PROTO "udp"
63 # define DATE_PORT "time"
64
65
66 int sock;
67 int sock_raw;
68 char myname[MAXHOSTNAMELEN + 1];
69 struct hostent *hp;
70 struct sockaddr_in server;
71 struct sockaddr_in dayaddr;
72 extern int measure_delta;
73
74 void bytenetorder(struct tsp *);
75 void bytehostorder(struct tsp *);
76
77
78 #define BU ((unsigned long)2208988800U) /* seconds before UNIX epoch */
79
80
81 /* compute the difference between our date and another machine
82 */
83 static int /* difference in days from our time */
84 daydiff(char *hostname)
85 {
86 int i;
87 int trials;
88 int tout;
89 struct timeval now;
90 struct pollfd set[1];
91 struct sockaddr from;
92 int fromlen;
93 unsigned long sec;
94
95
96 /* wait 2 seconds between 10 tries */
97 tout = 2000;
98 set[0].fd = sock;
99 set[0].events = POLLIN;
100 for (trials = 0; trials < 10; trials++) {
101 /* ask for the time */
102 sec = 0;
103 if (sendto(sock, &sec, sizeof(sec), 0,
104 (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) {
105 perror("sendto(sock)");
106 return 0;
107 }
108
109 for (;;) {
110 i = poll(set, 1, tout);
111 if (i < 0) {
112 if (errno == EINTR)
113 continue;
114 perror("poll(date read)");
115 return 0;
116 }
117 if (0 == i)
118 break;
119
120 fromlen = sizeof(from);
121 if (recvfrom(sock,&sec,sizeof(sec),0,
122 &from,&fromlen) < 0) {
123 perror("recvfrom(date read)");
124 return 0;
125 }
126
127 sec = ntohl(sec);
128 if (sec < BU) {
129 fprintf(stderr,
130 "%s says it is before 1970: %lu",
131 hostname, sec);
132 return 0;
133 }
134 sec -= BU;
135
136 (void)gettimeofday(&now, (struct timezone*)0);
137 return (sec - now.tv_sec);
138 }
139 }
140
141 /* if we get here, we tried too many times */
142 fprintf(stderr,"%s will not tell us the date\n", hostname);
143 return 0;
144 }
145
146
147 /*
148 * Clockdiff computes the difference between the time of the machine on
149 * which it is called and the time of the machines given as argument.
150 * The time differences measured by clockdiff are obtained using a sequence
151 * of ICMP TSTAMP messages which are returned to the sender by the IP module
152 * in the remote machine.
153 * In order to compare clocks of machines in different time zones, the time
154 * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
155 * If a hosts uses a different time format, it should set the high order
156 * bit of the 32-bit quantity it transmits.
157 * However, VMS apparently transmits the time in milliseconds since midnight
158 * local time (rather than GMT) without setting the high order bit.
159 * Furthermore, it does not understand daylight-saving time. This makes
160 * clockdiff behaving inconsistently with hosts running VMS.
161 *
162 * In order to reduce the sensitivity to the variance of message transmission
163 * time, clockdiff sends a sequence of messages. Yet, measures between
164 * two `distant' hosts can be affected by a small error. The error can,
165 * however, be reduced by increasing the number of messages sent in each
166 * measurement.
167 */
168 void
169 clockdiff(int argc, char *argv[])
170 {
171 int measure_status;
172 extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
173 register int avg_cnt;
174 register long avg;
175 struct servent *sp;
176
177 if (argc < 2) {
178 printf("Usage: clockdiff host ... \n");
179 return;
180 }
181
182 (void)gethostname(myname,sizeof(myname));
183 myname[sizeof(myname) - 1] = '\0';
184
185 /* get the address for the date ready */
186 sp = getservbyname(DATE_PORT, DATE_PROTO);
187 if (!sp) {
188 (void)fprintf(stderr, "%s/%s is an unknown service\n",
189 DATE_PORT, DATE_PROTO);
190 dayaddr.sin_port = 0;
191 } else {
192 dayaddr.sin_port = sp->s_port;
193 }
194
195 while (argc > 1) {
196 argc--; argv++;
197 hp = gethostbyname(*argv);
198 if (hp == NULL) {
199 fprintf(stderr, "timedc: %s: ", *argv);
200 herror(0);
201 continue;
202 }
203
204 server.sin_family = hp->h_addrtype;
205 bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length);
206 for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
207 measure_status = measure(10000,100, *argv, &server, 1);
208 if (measure_status != GOOD)
209 break;
210 avg += measure_delta;
211 }
212 if (measure_status == GOOD)
213 measure_delta = avg/avg_cnt;
214
215 switch (measure_status) {
216 case HOSTDOWN:
217 printf("%s is down\n", hp->h_name);
218 continue;
219 case NONSTDTIME:
220 printf("%s transmitts a non-standard time format\n",
221 hp->h_name);
222 continue;
223 case UNREACHABLE:
224 printf("%s is unreachable\n", hp->h_name);
225 continue;
226 }
227
228 /*
229 * Try to get the date only after using ICMP timestamps to
230 * get the time. This is because the date protocol
231 * is optional.
232 */
233 if (dayaddr.sin_port != 0) {
234 dayaddr.sin_family = hp->h_addrtype;
235 bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr,
236 hp->h_length);
237 avg = daydiff(*argv);
238 if (avg > SECDAY) {
239 printf("time on %s is %ld days ahead %s\n",
240 hp->h_name, avg/SECDAY, myname);
241 continue;
242 } else if (avg < -SECDAY) {
243 printf("time on %s is %ld days behind %s\n",
244 hp->h_name, -avg/SECDAY, myname);
245 continue;
246 }
247 }
248
249 if (measure_delta > 0) {
250 printf("time on %s is %d ms. ahead of time on %s\n",
251 hp->h_name, measure_delta, myname);
252 } else if (measure_delta == 0) {
253 printf("%s and %s have the same time\n",
254 hp->h_name, myname);
255 } else {
256 printf("time on %s is %d ms. behind time on %s\n",
257 hp->h_name, -measure_delta, myname);
258 }
259 }
260 return;
261 }
262
263
264 /*
265 * finds location of master timedaemon
266 */
267 void
268 msite(int argc, char *argv[])
269 {
270 int cc;
271 struct pollfd set[1];
272 struct sockaddr_in dest;
273 int i, length;
274 struct sockaddr from;
275 int tout;
276 struct tsp msg;
277 struct servent *srvp;
278 char *tgtname;
279
280 if (argc < 1) {
281 printf("Usage: msite [hostname]\n");
282 return;
283 }
284
285 srvp = getservbyname("timed", "udp");
286 if (srvp == 0) {
287 fprintf(stderr, "udp/timed: unknown service\n");
288 return;
289 }
290 dest.sin_port = srvp->s_port;
291 dest.sin_family = AF_INET;
292
293 (void)gethostname(myname, sizeof(myname));
294 i = 1;
295 tout = 15000;
296 set[0].fd = sock;
297 set[0].events = POLLIN;
298 do {
299 tgtname = (i >= argc) ? myname : argv[i];
300 hp = gethostbyname(tgtname);
301 if (hp == 0) {
302 fprintf(stderr, "timedc: %s: ", tgtname);
303 herror(0);
304 continue;
305 }
306 bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
307
308 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name) - 1);
309 msg.tsp_type = TSP_MSITE;
310 msg.tsp_vers = TSPVERSION;
311 bytenetorder(&msg);
312 if (sendto(sock, &msg, sizeof(struct tsp), 0,
313 (struct sockaddr*)&dest,
314 sizeof(struct sockaddr)) < 0) {
315 perror("sendto");
316 continue;
317 }
318
319 if (poll(set, 1, tout)) {
320 length = sizeof(struct sockaddr);
321 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
322 &from, &length);
323 if (cc < 0) {
324 perror("recvfrom");
325 continue;
326 }
327 bytehostorder(&msg);
328 if (msg.tsp_type == TSP_ACK) {
329 printf("master timedaemon at %s is %s\n",
330 tgtname, msg.tsp_name);
331 } else {
332 printf("received wrong ack: %s\n",
333 tsptype[msg.tsp_type]);
334 }
335 } else {
336 printf("communication error with %s\n", tgtname);
337 }
338 } while (++i < argc);
339 }
340
341 /*
342 * quits timedc
343 */
344 void
345 quit(int argc, char *argv[])
346 {
347 exit(0);
348 }
349
350
351 /*
352 * Causes the election timer to expire on the selected hosts
353 * It sends just one udp message per machine, relying on
354 * reliability of communication channel.
355 */
356 void
357 testing(int argc, char *argv[])
358 {
359 struct servent *srvp;
360 struct sockaddr_in sin;
361 struct tsp msg;
362
363 if (argc < 2) {
364 printf("Usage: election host1 [host2 ...]\n");
365 return;
366 }
367
368 srvp = getservbyname("timed", "udp");
369 if (srvp == 0) {
370 fprintf(stderr, "udp/timed: unknown service\n");
371 return;
372 }
373
374 while (argc > 1) {
375 argc--; argv++;
376 hp = gethostbyname(*argv);
377 if (hp == NULL) {
378 fprintf(stderr, "timedc: %s: ", *argv);
379 herror(0);
380 argc--; argv++;
381 continue;
382 }
383 sin.sin_port = srvp->s_port;
384 sin.sin_family = hp->h_addrtype;
385 bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
386
387 msg.tsp_type = TSP_TEST;
388 msg.tsp_vers = TSPVERSION;
389 (void)gethostname(myname, sizeof(myname));
390 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
391 bytenetorder(&msg);
392 if (sendto(sock, &msg, sizeof(struct tsp), 0,
393 (struct sockaddr*)&sin,
394 sizeof(struct sockaddr)) < 0) {
395 perror("sendto");
396 }
397 }
398 }
399
400
401 /*
402 * Enables or disables tracing on local timedaemon
403 */
404 void
405 tracing(int argc, char *argv[])
406 {
407 int onflag;
408 int length;
409 int cc;
410 struct pollfd set[1];
411 struct sockaddr_in dest;
412 struct sockaddr from;
413 int tout;
414 struct tsp msg;
415 struct servent *srvp;
416
417 if (argc != 2) {
418 printf("Usage: tracing { on | off }\n");
419 return;
420 }
421
422 srvp = getservbyname("timed", "udp");
423 if (srvp == 0) {
424 fprintf(stderr, "udp/timed: unknown service\n");
425 return;
426 }
427 dest.sin_port = srvp->s_port;
428 dest.sin_family = AF_INET;
429
430 (void)gethostname(myname,sizeof(myname));
431 hp = gethostbyname(myname);
432 bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
433
434 if (strcmp(argv[1], "on") == 0) {
435 msg.tsp_type = TSP_TRACEON;
436 onflag = ON;
437 } else {
438 msg.tsp_type = TSP_TRACEOFF;
439 onflag = OFF;
440 }
441
442 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name) - 1);
443 msg.tsp_vers = TSPVERSION;
444 bytenetorder(&msg);
445 if (sendto(sock, &msg, sizeof(struct tsp), 0,
446 (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
447 perror("sendto");
448 return;
449 }
450
451 tout = 5000;
452 set[0].fd = sock;
453 set[0].events = POLLIN;
454 if (poll(set, 1, tout)) {
455 length = sizeof(struct sockaddr);
456 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
457 &from, &length);
458 if (cc < 0) {
459 perror("recvfrom");
460 return;
461 }
462 bytehostorder(&msg);
463 if (msg.tsp_type == TSP_ACK)
464 if (onflag)
465 printf("timed tracing enabled\n");
466 else
467 printf("timed tracing disabled\n");
468 else
469 printf("wrong ack received: %s\n",
470 tsptype[msg.tsp_type]);
471 } else
472 printf("communication error\n");
473 }
474
475 int
476 priv_resources(void)
477 {
478 int port;
479 struct sockaddr_in sin;
480
481 sock = socket(AF_INET, SOCK_DGRAM, 0);
482 if (sock < 0) {
483 perror("opening socket");
484 return(-1);
485 }
486
487 sin.sin_family = AF_INET;
488 sin.sin_addr.s_addr = 0;
489 for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
490 sin.sin_port = htons((u_short)port);
491 if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
492 break;
493 if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
494 perror("bind");
495 (void) close(sock);
496 return(-1);
497 }
498 }
499 if (port == IPPORT_RESERVED / 2) {
500 fprintf(stderr, "all reserved ports in use\n");
501 (void) close(sock);
502 return(-1);
503 }
504
505 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
506 if (sock_raw < 0) {
507 perror("opening raw socket");
508 (void) close(sock);
509 return(-1);
510 }
511 return(1);
512 }
513