/* * 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_storage *a, struct sockaddr_storage *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 addrinfo *serv_ail, *serv_aip; struct addrinfo *my_ail, *my_aip; struct sockaddr_storage resp_addr; struct addrinfo hints; socklen_t resp_addr_len; unsigned int btime = 0; ssize_t nbytes; int retry; fd_set sdset; struct timeval timeout; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; if (getaddrinfo(host, "5016", &hints, &serv_ail) != 0) { fprintf(stderr, "Could not get address info for %s: %s\n", host, strerror(errno)); return 0; } for (serv_aip = serv_ail; serv_aip != NULL; serv_aip = serv_aip->ai_next) { if ((sd = socket(serv_aip->ai_family, serv_aip->ai_socktype, serv_aip->ai_protocol)) < 0) { continue; } break; } if (serv_aip == NULL) { fprintf(stderr, "Could not create socket: %s\n", strerror(errno)); freeaddrinfo(serv_ail); return 0; } memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; /* Fill in my IP for me */ if (getaddrinfo(NULL, "0", &hints, &my_ail) != 0) { fprintf(stderr, "Could not get address info for myself!: %s\n", strerror(errno)); return 0; } for (my_aip = my_ail; my_aip != NULL; my_aip = my_aip->ai_next) { if (bind(sd, my_aip->ai_addr, my_aip->ai_addrlen) < 0) { continue; } break; } if (my_aip == NULL) { fprintf(stderr, "Could not bind to local address: %s\n", strerror(errno)); freeaddrinfo(my_ail); return 0; } memset(cookie, 0, BTIME_MSGLEN); gen_cookie(cookie); for (retry = 0; retry < MAX_RETRY; retry++) { if ((nbytes = sendto(sd, &cookie, BTIME_MSGLEN, MSG_DONTWAIT, serv_aip->ai_addr, serv_aip->ai_addrlen)) < 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 = 500000; 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((struct sockaddr_storage *)serv_aip->ai_addr, &resp_addr)) { /* If no cookie, check peer */ char rstr[NI_MAXHOST]; char sstr[NI_MAXHOST]; inet_ntop(resp_addr.ss_family, &resp_addr, rstr, NI_MAXHOST); inet_ntop(serv_aip->ai_family, serv_aip->ai_addr, sstr, NI_MAXHOST); fprintf(stderr, "Got response from %s instead of %s\n", rstr, sstr); retry = 1; continue; } else { /* If peer matches, accept the bare btime */ btime = strtoul(response, (char **)NULL, 10); break; } } while (retry); close(sd); freeaddrinfo(serv_ail); freeaddrinfo(my_ail); return btime; } /* same_addr * * Compare two struct sockaddr_storage * * Returns: * 1 if the addresses match * 0 if the addresses are different */ static int same_addr(struct sockaddr_storage *a, struct sockaddr_storage *b) { return 1; if (a->ss_family != b->ss_family) { return 0; } else if ((a->ss_family == AF_INET) && (((struct sockaddr_in *)a)->sin_addr.s_addr != ((struct sockaddr_in *)b)->sin_addr.s_addr)) { return 0; } else if ((a->ss_family == AF_INET6) && (((struct sockaddr_in6 *)a)->sin6_addr.s6_addr != ((struct sockaddr_in6 *)b)->sin6_addr.s6_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'; }