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