/* * 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 void gen_cookie(char *s); /* * btime_init -- * * Prepare a socket to use for getting boot time from btimed */ int btime_init(const char *host) { int sd = 0; struct addrinfo *serv_ail, *serv_aip; struct addrinfo *my_ail, *my_aip; struct addrinfo hints; 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)); sd = 0; goto free_serv; } 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)); sd = 0; goto free_serv; } 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)); sd = 0; goto free_my; } if (connect(sd, serv_aip->ai_addr, serv_aip->ai_addrlen) < 0) { fprintf(stderr, "Could not connect to host: %s\n", strerror(errno)); sd = 0; } free_my: freeaddrinfo(my_ail); free_serv: freeaddrinfo(serv_ail); return sd; } /* * btime_do -- * * Get the boot time from btimed using an already prepared socket * * Returns: * btime, or 0 on failure. */ unsigned int btime_do(const int sd) { char cookie[BTIME_MSGLEN]; char response[BTIME_MSGLEN]; unsigned int btime = 0; ssize_t nbytes; int retry; fd_set sdset; struct timeval timeout; memset(cookie, 0, BTIME_MSGLEN); gen_cookie(cookie); for (retry = 0; retry < MAX_RETRY; retry++) { if ((nbytes = send(sd, &cookie, BTIME_MSGLEN, MSG_DONTWAIT)) < 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(response, 0, BTIME_MSGLEN); nbytes = recv(sd, &response, BTIME_MSGLEN, MSG_DONTWAIT); if (nbytes == 0) continue; /* Nothing received */ 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; } } } while (retry); return btime; } /* * 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 s; unsigned int b; s = btime_init(host); b = btime_do(s); close(s); return b; } /* 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'; }