1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)netdate.c 5.3 (Berkeley) 04/28/93"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/time.h> 14 #include <sys/socket.h> 15 16 #include <netinet/in.h> 17 #include <netdb.h> 18 #define TSPTYPES 19 #include <protocols/timed.h> 20 21 #include <err.h> 22 #include <errno.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "extern.h" 28 29 #define WAITACK 2 /* seconds */ 30 #define WAITDATEACK 5 /* seconds */ 31 32 extern int retval; 33 34 /* 35 * Set the date in the machines controlled by timedaemons by communicating the 36 * new date to the local timedaemon. If the timedaemon is in the master state, 37 * it performs the correction on all slaves. If it is in the slave state, it 38 * notifies the master that a correction is needed. 39 * Returns 0 on success. Returns > 0 on failure, setting retval to 2; 40 */ 41 int 42 netsettime(tval) 43 time_t tval; 44 { 45 struct timeval tout; 46 struct servent *sp; 47 struct tsp msg; 48 struct sockaddr_in sin, dest, from; 49 fd_set ready; 50 long waittime; 51 int s, length, port, timed_ack, found, err; 52 char hostname[MAXHOSTNAMELEN]; 53 54 if ((sp = getservbyname("timed", "udp")) == NULL) { 55 warnx("udp/timed: unknown service"); 56 return (retval = 2); 57 } 58 59 dest.sin_port = sp->s_port; 60 dest.sin_family = AF_INET; 61 dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY); 62 s = socket(AF_INET, SOCK_DGRAM, 0); 63 if (s < 0) { 64 if (errno != EPROTONOSUPPORT) 65 warn("timed"); 66 return (retval = 2); 67 } 68 69 memset(&sin, 0, sizeof(sin)); 70 sin.sin_family = AF_INET; 71 for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { 72 sin.sin_port = htons((u_short)port); 73 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 74 break; 75 if (errno == EADDRINUSE) 76 continue; 77 if (errno != EADDRNOTAVAIL) 78 warn("bind"); 79 goto bad; 80 } 81 if (port == IPPORT_RESERVED / 2) { 82 warnx("all ports in use"); 83 goto bad; 84 } 85 msg.tsp_type = TSP_SETDATE; 86 msg.tsp_vers = TSPVERSION; 87 if (gethostname(hostname, sizeof(hostname))) { 88 warn("gethostname"); 89 goto bad; 90 } 91 (void)strncpy(msg.tsp_name, hostname, sizeof(hostname)); 92 msg.tsp_seq = htons((u_short)0); 93 msg.tsp_time.tv_sec = htonl((u_long)tval); 94 msg.tsp_time.tv_usec = htonl((u_long)0); 95 length = sizeof(struct sockaddr_in); 96 if (connect(s, (struct sockaddr *)&dest, length) < 0) { 97 warn("connect"); 98 goto bad; 99 } 100 if (send(s, (char *)&msg, sizeof(struct tsp), 0) < 0) { 101 if (errno != ECONNREFUSED) 102 warn("send"); 103 goto bad; 104 } 105 106 timed_ack = -1; 107 waittime = WAITACK; 108 loop: 109 tout.tv_sec = waittime; 110 tout.tv_usec = 0; 111 112 FD_ZERO(&ready); 113 FD_SET(s, &ready); 114 found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout); 115 116 length = sizeof(err); 117 if (!getsockopt(s, 118 SOL_SOCKET, SO_ERROR, (char *)&err, &length) && err) { 119 if (err != ECONNREFUSED) 120 warn("send (delayed error)"); 121 goto bad; 122 } 123 124 if (found > 0 && FD_ISSET(s, &ready)) { 125 length = sizeof(struct sockaddr_in); 126 if (recvfrom(s, &msg, sizeof(struct tsp), 0, 127 (struct sockaddr *)&from, &length) < 0) { 128 if (errno != ECONNREFUSED) 129 warn("recvfrom"); 130 goto bad; 131 } 132 msg.tsp_seq = ntohs(msg.tsp_seq); 133 msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec); 134 msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec); 135 switch (msg.tsp_type) { 136 case TSP_ACK: 137 timed_ack = TSP_ACK; 138 waittime = WAITDATEACK; 139 goto loop; 140 case TSP_DATEACK: 141 (void)close(s); 142 return (0); 143 default: 144 warnx("wrong ack received from timed: %s", 145 tsptype[msg.tsp_type]); 146 timed_ack = -1; 147 break; 148 } 149 } 150 if (timed_ack == -1) 151 warnx("can't reach time daemon, time set locally"); 152 153 bad: 154 (void)close(s); 155 return (retval = 2); 156 } 157