cmds.c revision 1.20 1 /* $NetBSD: cmds.c,v 1.20 2007/01/25 23:25:20 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.20 2007/01/25 23:25:20 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 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 ((unsigned long)2208988800U) /* 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 from;
93 socklen_t fromlen;
94 unsigned long 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 /* ask for the time */
103 sec = 0;
104 if (sendto(sock, &sec, sizeof(sec), 0,
105 (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) {
106 warn("sendto(sock)");
107 return 0;
108 }
109
110 for (;;) {
111 i = poll(set, 1, tout);
112 if (i < 0) {
113 if (errno == EINTR)
114 continue;
115 warn("poll(date read)");
116 return 0;
117 }
118 if (0 == i)
119 break;
120
121 fromlen = sizeof(from);
122 if (recvfrom(sock,&sec,sizeof(sec),0,
123 &from,&fromlen) < 0) {
124 warn("recvfrom(date read)");
125 return 0;
126 }
127
128 sec = ntohl(sec);
129 if (sec < BU) {
130 warnx("%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 warnx("%s will not tell us the date", 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 int avg_cnt;
174 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 warnx("%s/%s is an unknown service", DATE_PORT, DATE_PROTO);
189 dayaddr.sin_port = 0;
190 } else {
191 dayaddr.sin_port = sp->s_port;
192 }
193
194 while (argc > 1) {
195 argc--; argv++;
196 hp = gethostbyname(*argv);
197 if (hp == NULL) {
198 warnx("Error resolving %s (%s)", *argv,
199 hstrerror(h_errno));
200 continue;
201 }
202
203 server.sin_family = hp->h_addrtype;
204 memcpy(&server.sin_addr.s_addr, hp->h_addr, hp->h_length);
205 for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
206 measure_status = measure(10000,100, *argv, &server, 1);
207 if (measure_status != GOOD)
208 break;
209 avg += measure_delta;
210 }
211 if (measure_status == GOOD)
212 measure_delta = avg/avg_cnt;
213
214 switch (measure_status) {
215 case HOSTDOWN:
216 printf("%s is down\n", hp->h_name);
217 continue;
218 case NONSTDTIME:
219 printf("%s transmits a non-standard time format\n",
220 hp->h_name);
221 continue;
222 case UNREACHABLE:
223 printf("%s is unreachable\n", hp->h_name);
224 continue;
225 }
226
227 /*
228 * Try to get the date only after using ICMP timestamps to
229 * get the time. This is because the date protocol
230 * is optional.
231 */
232 if (dayaddr.sin_port != 0) {
233 dayaddr.sin_family = hp->h_addrtype;
234 memcpy(&dayaddr.sin_addr.s_addr, hp->h_addr,
235 hp->h_length);
236 avg = daydiff(*argv);
237 if (avg > SECDAY) {
238 printf("time on %s is %ld days ahead %s\n",
239 hp->h_name, avg/SECDAY, myname);
240 continue;
241 } else if (avg < -SECDAY) {
242 printf("time on %s is %ld days behind %s\n",
243 hp->h_name, -avg/SECDAY, myname);
244 continue;
245 }
246 }
247
248 if (measure_delta > 0) {
249 printf("time on %s is %d ms. ahead of time on %s\n",
250 hp->h_name, measure_delta, myname);
251 } else if (measure_delta == 0) {
252 printf("%s and %s have the same time\n",
253 hp->h_name, myname);
254 } else {
255 printf("time on %s is %d ms. behind time on %s\n",
256 hp->h_name, -measure_delta, myname);
257 }
258 }
259 return;
260 }
261
262
263 /*
264 * finds location of master timedaemon
265 */
266 void
267 msite(int argc, char *argv[])
268 {
269 int cc;
270 struct pollfd set[1];
271 struct sockaddr_in dest;
272 int i;
273 socklen_t 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 warnx("udp/timed: unknown service");
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 warnx("Error resolving %s (%s)", tgtname,
303 hstrerror(h_errno));
304 continue;
305 }
306 memcpy(&dest.sin_addr.s_addr, hp->h_addr, hp->h_length);
307
308 set_tsp_name(&msg, myname);
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 warn("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 warn("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 (void) argc;
348 (void) argv;
349 exit(0);
350 }
351
352
353 /*
354 * Causes the election timer to expire on the selected hosts
355 * It sends just one udp message per machine, relying on
356 * reliability of communication channel.
357 */
358 void
359 testing(int argc, char *argv[])
360 {
361 struct servent *srvp;
362 struct sockaddr_in addr;
363 struct tsp msg;
364
365 if (argc < 2) {
366 printf("Usage: election host1 [host2 ...]\n");
367 return;
368 }
369
370 srvp = getservbyname("timed", "udp");
371 if (srvp == 0) {
372 warnx("udp/timed: unknown service");
373 return;
374 }
375
376 while (argc > 1) {
377 argc--; argv++;
378 hp = gethostbyname(*argv);
379 if (hp == NULL) {
380 warnx("Error resolving %s (%s)", *argv,
381 hstrerror(h_errno));
382 argc--; argv++;
383 continue;
384 }
385 addr.sin_port = srvp->s_port;
386 addr.sin_family = hp->h_addrtype;
387 memcpy(&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
388
389 msg.tsp_type = TSP_TEST;
390 msg.tsp_vers = TSPVERSION;
391 (void)gethostname(myname, sizeof(myname));
392 set_tsp_name(&msg, myname);
393 bytenetorder(&msg);
394 if (sendto(sock, &msg, sizeof(struct tsp), 0,
395 (struct sockaddr*)&addr,
396 sizeof(struct sockaddr)) < 0) {
397 warn("sendto");
398 }
399 }
400 }
401
402
403 /*
404 * Enables or disables tracing on local timedaemon
405 */
406 void
407 tracing(int argc, char *argv[])
408 {
409 int onflag;
410 socklen_t length;
411 int cc;
412 struct pollfd set[1];
413 struct sockaddr_in dest;
414 struct sockaddr from;
415 int tout;
416 struct tsp msg;
417 struct servent *srvp;
418
419 if (argc != 2) {
420 printf("Usage: tracing { on | off }\n");
421 return;
422 }
423
424 srvp = getservbyname("timed", "udp");
425 if (srvp == 0) {
426 warnx("udp/timed: unknown service");
427 return;
428 }
429 dest.sin_port = srvp->s_port;
430 dest.sin_family = AF_INET;
431
432 (void)gethostname(myname,sizeof(myname));
433 hp = gethostbyname(myname);
434 memcpy(&dest.sin_addr.s_addr, hp->h_addr, hp->h_length);
435
436 if (strcmp(argv[1], "on") == 0) {
437 msg.tsp_type = TSP_TRACEON;
438 onflag = ON;
439 } else {
440 msg.tsp_type = TSP_TRACEOFF;
441 onflag = OFF;
442 }
443
444 set_tsp_name(&msg, myname);
445 msg.tsp_vers = TSPVERSION;
446 bytenetorder(&msg);
447 if (sendto(sock, &msg, sizeof(struct tsp), 0,
448 (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
449 warn("sendto");
450 return;
451 }
452
453 tout = 5000;
454 set[0].fd = sock;
455 set[0].events = POLLIN;
456 if (poll(set, 1, tout)) {
457 length = sizeof(struct sockaddr);
458 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
459 &from, &length);
460 if (cc < 0) {
461 warn("recvfrom");
462 return;
463 }
464 bytehostorder(&msg);
465 if (msg.tsp_type == TSP_ACK)
466 if (onflag)
467 printf("timed tracing enabled\n");
468 else
469 printf("timed tracing disabled\n");
470 else
471 printf("wrong ack received: %s\n",
472 tsptype[msg.tsp_type]);
473 } else
474 printf("communication error\n");
475 }
476
477 int
478 priv_resources(void)
479 {
480 struct sockaddr_in addr;
481
482 sock = socket(AF_INET, SOCK_DGRAM, 0);
483 if (sock < 0) {
484 perror("opening socket");
485 return -1;
486 }
487
488 memset(&addr, 0, sizeof(addr));
489 addr.sin_family = AF_INET;
490
491 if ((bindresvport(sock, &addr)) == -1) {
492 warn("Failed opening reserved port");
493 (void)close(sock);
494 return -1;
495 }
496
497 if ((sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) {
498 warn("Cannot open raw socket");
499 (void)close(sock);
500 return -1;
501 }
502 return 1;
503 }
504