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