#include "namespace.h"
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/signal.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpc/rpc.h>
#include <rpc/rpc_com.h>
#include <rpc/rpcb_prot.h>
#undef NIS
#include <rpcsvc/nis.h>
#include "un-namespace.h"
extern int _rpc_dtablesize( void );
#ifdef TESTING
#define msg(x) printf("ERROR: %s\n", x)
#else
#define msg(x)
#endif
static int saw_alarm = 0;
static void
alarm_hndler(int s)
{
saw_alarm = 1;
return;
}
#define NYEARS (1970 - 1900)
#define TOFFSET ((u_long)60*60*24*(365*NYEARS + (NYEARS/4)))
static int uaddr_to_sockaddr(char *uaddr, struct sockaddr_in *sin)
{
unsigned char p_bytes[2];
int i;
unsigned long a[6];
i = sscanf(uaddr, "%lu.%lu.%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2],
&a[3], &a[4], &a[5]);
if (i < 6)
return(1);
for (i = 0; i < 4; i++)
sin->sin_addr.s_addr |= (a[i] & 0x000000FF) << (8 * i);
p_bytes[0] = (unsigned char)a[4] & 0x000000FF;
p_bytes[1] = (unsigned char)a[5] & 0x000000FF;
sin->sin_family = AF_INET;
bcopy((char *)&p_bytes, (char *)&sin->sin_port, 2);
return (0);
}
static void
free_eps(endpoint eps[], int num)
{
int i;
for (i = 0; i < num; i++) {
free(eps[i].uaddr);
free(eps[i].proto);
free(eps[i].family);
}
return;
}
static nis_server *
get_server(struct sockaddr_in *sin, char *host, nis_server *srv,
endpoint eps[], int maxep)
{
char hname[256];
int num_ep = 0, i;
struct hostent *he;
struct hostent dummy;
char *ptr[2];
endpoint *ep;
if (host == NULL && sin == NULL)
return (NULL);
if (sin == NULL) {
he = gethostbyname(host);
if (he == NULL)
return(NULL);
} else {
he = &dummy;
ptr[0] = (char *)&sin->sin_addr.s_addr;
ptr[1] = NULL;
dummy.h_addr_list = ptr;
}
for (i = 0, ep = eps; (he->h_addr_list[i] != NULL) && (num_ep < maxep);
i++, ep++, num_ep++) {
struct in_addr *a;
a = (struct in_addr *)he->h_addr_list[i];
snprintf(hname, sizeof(hname), "%s.0.111", inet_ntoa(*a));
ep->uaddr = strdup(hname);
ep->family = strdup("inet");
ep->proto = strdup("tcp");
if (ep->uaddr == NULL || ep->family == NULL || ep->proto == NULL) {
free_eps(eps, num_ep + 1);
return (NULL);
}
}
for (i = 0; (he->h_addr_list[i] != NULL) && (num_ep < maxep);
i++, ep++, num_ep++) {
struct in_addr *a;
a = (struct in_addr *)he->h_addr_list[i];
snprintf(hname, sizeof(hname), "%s.0.111", inet_ntoa(*a));
ep->uaddr = strdup(hname);
ep->family = strdup("inet");
ep->proto = strdup("udp");
if (ep->uaddr == NULL || ep->family == NULL || ep->proto == NULL) {
free_eps(eps, num_ep + 1);
return (NULL);
}
}
srv->name = (nis_name) host;
srv->ep.ep_len = num_ep;
srv->ep.ep_val = eps;
srv->key_type = NIS_PK_NONE;
srv->pkey.n_bytes = NULL;
srv->pkey.n_len = 0;
return (srv);
}
int
__rpc_get_time_offset(struct timeval *td, nis_server *srv, char *thost,
char **uaddr, struct sockaddr_in *netid)
{
CLIENT *clnt;
endpoint *ep,
*useep = NULL;
char *useua = NULL;
int epl, i;
enum clnt_stat status;
u_long thetime, delta;
int needfree = 0;
struct timeval tv;
int time_valid;
int udp_ep = -1, tcp_ep = -1;
int a1, a2, a3, a4;
char ut[64], ipuaddr[64];
endpoint teps[32];
nis_server tsrv;
void (*oldsig)(int) = NULL;
struct sockaddr_in sin;
socklen_t len;
int s = RPC_ANYSOCK;
int type = 0;
td->tv_sec = 0;
td->tv_usec = 0;
if (*uaddr == NULL) {
if ((srv != NULL) && (thost != NULL)) {
msg("both timehost and srv pointer used!");
return (0);
}
if (! srv) {
srv = get_server(netid, thost, &tsrv, teps, 32);
if (srv == NULL) {
msg("unable to contruct server data.");
return (0);
}
needfree = 1;
}
ep = srv->ep.ep_val;
epl = srv->ep.ep_len;
for (i = 0;
(i < epl) && ((udp_ep == -1) || (tcp_ep == -1)); i++) {
if (strcasecmp(ep[i].proto, "udp") == 0)
udp_ep = i;
if (strcasecmp(ep[i].proto, "tcp") == 0)
tcp_ep = i;
}
if (tcp_ep > -1) {
useep = &ep[tcp_ep];
useua = ep[tcp_ep].uaddr;
type = SOCK_STREAM;
} else if (udp_ep > -1) {
useep = &ep[udp_ep];
useua = ep[udp_ep].uaddr;
type = SOCK_DGRAM;
}
if (useep == NULL) {
msg("no acceptable transport endpoints.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
}
if (*uaddr != NULL)
useua = *uaddr;
sscanf(useua, "%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
sprintf(ipuaddr, "%d.%d.%d.%d.0.111", a1, a2, a3, a4);
useua = &ipuaddr[0];
bzero((char *)&sin, sizeof(sin));
if (uaddr_to_sockaddr(useua, &sin)) {
msg("unable to translate uaddr to sockaddr.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
clnt = clnttcp_create(&sin, RPCBPROG, RPCBVERS, &s, 0, 0);
if (clnt == NULL) {
msg("unable to create client handle to rpcbind.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
tv.tv_sec = 5;
tv.tv_usec = 0;
time_valid = 0;
status = clnt_call(clnt, RPCBPROC_GETTIME, (xdrproc_t)xdr_void, NULL,
(xdrproc_t)xdr_u_long, &thetime, tv);
if (status == RPC_SUCCESS)
time_valid = 1;
else {
int save;
if (clnt != NULL) {
clnt_destroy(clnt);
clnt = NULL;
}
sscanf(useua, "%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
sprintf(ut, "%d.%d.%d.%d.0.37", a1, a2, a3, a4);
if (uaddr_to_sockaddr(ut, &sin)) {
msg("cannot convert timeservice uaddr to sockaddr.");
goto error;
}
s = _socket(AF_INET, type, 0);
if (s == -1) {
msg("unable to open fd to network.");
goto error;
}
if (type == SOCK_DGRAM) {
struct timeval timeout = { 20, 0 };
struct sockaddr_in from;
fd_set readfds;
int res;
if (_sendto(s, &thetime, sizeof(thetime), 0,
(struct sockaddr *)&sin, sizeof(sin)) == -1) {
msg("udp : sendto failed.");
goto error;
}
do {
FD_ZERO(&readfds);
FD_SET(s, &readfds);
res = _select(_rpc_dtablesize(), &readfds,
(fd_set *)NULL, (fd_set *)NULL, &timeout);
} while (res < 0 && errno == EINTR);
if (res <= 0)
goto error;
len = sizeof(from);
res = _recvfrom(s, (char *)&thetime, sizeof(thetime), 0,
(struct sockaddr *)&from, &len);
if (res == -1) {
msg("recvfrom failed on udp transport.");
goto error;
}
time_valid = 1;
} else {
int res;
oldsig = (void (*)(int))signal(SIGALRM, alarm_hndler);
saw_alarm = 0;
alarm(20);
res = _connect(s, (struct sockaddr *)&sin, sizeof(sin));
if (res == -1) {
msg("failed to connect to tcp endpoint.");
goto error;
}
if (saw_alarm) {
msg("alarm caught it, must be unreachable.");
goto error;
}
res = _read(s, (char *)&thetime, sizeof(thetime));
if (res != sizeof(thetime)) {
if (saw_alarm)
msg("timed out TCP call.");
else
msg("wrong size of results returned");
goto error;
}
time_valid = 1;
}
save = errno;
(void)_close(s);
errno = save;
s = RPC_ANYSOCK;
if (time_valid) {
thetime = ntohl(thetime);
thetime = thetime - TOFFSET;
} else
thetime = 0;
}
gettimeofday(&tv, 0);
error:
if (s != RPC_ANYSOCK)
(void)_close(s);
if (clnt != NULL)
clnt_destroy(clnt);
alarm(0);
if (oldsig) {
signal(SIGALRM, oldsig);
}
if (time_valid) {
if (*uaddr == NULL)
*uaddr = strdup(useua);
tv.tv_sec += (tv.tv_sec > 500000) ? 1 : 0;
delta = (thetime > tv.tv_sec) ? thetime - tv.tv_sec :
tv.tv_sec - thetime;
td->tv_sec = (thetime < tv.tv_sec) ? - delta : delta;
td->tv_usec = 0;
} else {
msg("unable to get the server's time.");
}
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (time_valid);
}