#include #include #include #include #include #include #include #include #include #include #include #include "btime_int.h" static int same_addr(struct sockaddr_in *a, struct sockaddr_in *b); /* * btime -- * * Return the boot time of a remote host if that host is running the * btimed deamon. * * Returns: * btime, or 0 on failure. */ unsigned int btime(const char *host) { int sd; char msg[BTIME_MSGLEN]; struct sockaddr_in serv_addr; struct sockaddr_in my_addr; struct sockaddr_in resp_addr; int serv_addr_len; int resp_addr_len; struct hostent *hostent; in_addr_t inaddr; unsigned int btime; ssize_t nbytes; int retry; if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { fprintf(stderr, "Could not create socket: %s\n", strerror(errno)); return 0; } memset(&serv_addr, 0, sizeof serv_addr); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(BTIME_PORT); if ((inaddr = inet_addr(host)) != INADDR_NONE) { memcpy(&serv_addr.sin_addr, &inaddr, sizeof inaddr); } else { if ((hostent = gethostbyname(host)) == NULL) { return 0; } memcpy(&serv_addr.sin_addr, hostent->h_addr, hostent->h_length); } memset(&my_addr, 0, sizeof my_addr); my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = htonl(INADDR_ANY); my_addr.sin_port = htons(0); if (bind(sd, (struct sockaddr *)&my_addr, sizeof my_addr) < 0) { fprintf(stderr, "Could not bind to local address: %s\n", strerror(errno)); return 0; } memset(msg, 0, BTIME_MSGLEN); for (retry = 0; retry < MAX_RETRY; retry++) { serv_addr_len = sizeof serv_addr; if ((nbytes = sendto(sd, &msg, BTIME_MSGLEN, MSG_DONTWAIT, (struct sockaddr *)&serv_addr, serv_addr_len)) < 0) { if (errno == EAGAIN) { usleep(retry * 100); } else { /* Non EAGAIN error... */ break; } } else { break; } } memset(&resp_addr, 0, sizeof resp_addr); memset(msg, 0, BTIME_MSGLEN); for (retry = 0; retry < MAX_RETRY; retry++) { resp_addr_len = sizeof resp_addr; if ((nbytes = recvfrom(sd, &msg, BTIME_MSGLEN, MSG_DONTWAIT, (struct sockaddr *)&resp_addr, &resp_addr_len)) < 0) { if (errno == EAGAIN) { usleep(retry * 100); } else { /* Non EAGAIN error... */ break; } } else if (!same_addr(&serv_addr, &resp_addr)) { char *rstr = strdup(inet_ntoa(resp_addr.sin_addr)); char *sstr = strdup(inet_ntoa(serv_addr.sin_addr)); fprintf(stderr, "Got response from %s instead of %s\n", rstr, sstr); free(rstr); free(sstr); continue; } else { break; } } if (nbytes == 0) { btime = 0; } else { btime = strtoul(msg, (char **)NULL, 10); } close(sd); return btime; } /* same_addr * * Compare to struct sockaddr_in's * * Returns: * 1 if the addresses match * 0 if the addresses are different */ static int same_addr(struct sockaddr_in *a, struct sockaddr_in *b) { if (a->sin_family != b->sin_family) { return 0; } else if (a->sin_addr.s_addr != b->sin_addr.s_addr) { return 0; } else { return 1; } }