/* * Copyright © 2005-2008 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions of the * GNU General Public License v.2. This program is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY expressed or implied, * including the implied warranties of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat * trademarks that are incorporated in the source code or documentation are not * subject to the GNU General Public License and may only be used or replicated * with the express permission of Red Hat, Inc. * * Red Hat Author(s): Nathan Straz * Dean Jansa */ #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); static void gen_cookie(char *s); /* * 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 cookie[BTIME_MSGLEN]; char response[BTIME_MSGLEN]; struct sockaddr_in serv_addr; struct sockaddr_in my_addr; struct sockaddr_in resp_addr; socklen_t serv_addr_len; socklen_t resp_addr_len; struct hostent *hostent; in_addr_t inaddr; unsigned int btime = 0; ssize_t nbytes; int retry; fd_set sdset; struct timeval timeout; 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(cookie, 0, BTIME_MSGLEN); gen_cookie(cookie); for (retry = 0; retry < MAX_RETRY; retry++) { serv_addr_len = sizeof serv_addr; if ((nbytes = sendto(sd, &cookie, 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; } } FD_ZERO(&sdset); FD_SET(sd, &sdset); do { retry = 0; timeout.tv_sec = 0; timeout.tv_usec = 250000; if (select(sd + 1, &sdset, NULL, NULL, &timeout) != 1) { /* Either an error or a timeout, we don't need to retry */ break; } memset(&resp_addr, 0, sizeof resp_addr); memset(response, 0, BTIME_MSGLEN); resp_addr_len = sizeof resp_addr; nbytes = recvfrom(sd, &response, BTIME_MSGLEN, MSG_DONTWAIT, (struct sockaddr *)&resp_addr, &resp_addr_len); if (nbytes == 0) { /* Nothing received */ continue; } else if (response[0] == 'B' && response[1] == 'T') { /* Check for new style cookie */ if (memcmp(cookie, response, COOKIE_LEN) == 0) { btime = strtoul(response+COOKIE_LEN, NULL, 10); break; } else { /* ignore the invalid btime cookie */ retry = 1; continue; } } else if (!same_addr(&serv_addr, &resp_addr)) { /* If no cookie, check peer */ 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); retry = 1; continue; } else { /* If peer matches, accept the bare btime */ btime = strtoul(response, (char **)NULL, 10); break; } } while (retry); 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; } } /* gen_cookie * * Generate a random string that btimed will send back to us. */ static void gen_cookie(char *s) { char *a = s; int i; *a++ = 'B'; *a++ = 'T'; for (i = 0; i < COOKIE_RANDOM_PARTS; i++) { *(int32_t *)a = (int32_t)random(); a += sizeof(int32_t); } *a = '\n'; }