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