#include "mt.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <netdir.h>
#include <string.h>
#include <strings.h>
#include <netconfig.h>
#include <netdb.h>
#include <signal.h>
#include <sys/errno.h>
#include <sys/poll.h>
#include <rpc/rpc.h>
#include <rpc/nettype.h>
#undef NIS
#include <rpcsvc/nis.h>
extern void __nis_netconfig2ep(struct netconfig *, endpoint *);
extern bool_t __nis_netconfig_matches_ep(struct netconfig *, endpoint *);
#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;
}
#define NYEARS (1970 - 1900)
#define TOFFSET ((uint_t)60*60*24*(365*NYEARS + (NYEARS/4)))
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);
}
}
static nis_server *
get_server(char *host, nis_server *srv, endpoint eps[], int maxep)
{
int num_ep = 0, i;
struct netconfig *nc;
void *nch;
struct nd_hostserv hs;
struct nd_addrlist *addrs;
if (! host)
return (NULL);
hs.h_host = host;
hs.h_serv = "rpcbind";
nch = setnetconfig();
while (nc = getnetconfig(nch)) {
if ((nc->nc_flag & NC_VISIBLE) == 0)
continue;
if (! netdir_getbyname(nc, &hs, &addrs)) {
for (i = 0; (i < (addrs->n_cnt)) && (num_ep < maxep);
i++, num_ep++) {
eps[num_ep].uaddr =
taddr2uaddr(nc, &(addrs->n_addrs[i]));
__nis_netconfig2ep(nc, &(eps[num_ep]));
}
netdir_free((char *)addrs, ND_ADDRLIST);
}
}
(void) endnetconfig(nch);
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);
}
#define MEP(ep, prot) (strcasecmp(ep.proto, prot) == 0)
#define MAX_ENDPOINTS 32
int
__rpc_get_time_offset(struct timeval *td, nis_server *srv,
char *thost, char **uaddr, char **netid)
{
CLIENT *clnt;
struct netbuf *addr = 0;
void *nc_handle;
struct netconfig *nc;
endpoint *ep;
char *useua = NULL,
*useid = NULL;
int epl, i;
enum clnt_stat status;
uint_t thetime;
ulong_t delta;
int needfree = 0;
struct timeval tv;
int rtime_fd = -1, time_valid, flag = 0;
int a1, a2, a3, a4;
char ut[INET6_ADDRSTRLEN];
char ipuaddr[INET6_ADDRSTRLEN];
endpoint teps[MAX_ENDPOINTS],
*epcand[MAX_ENDPOINTS],
*nonipcand[MAX_ENDPOINTS],
supplied;
uint32_t epc, nonip;
nis_server tsrv;
void (*oldsig)() = NULL;
char *dot = NULL;
nc = NULL;
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(thost, &tsrv, teps, 32);
if (! srv) {
msg("unable to contruct server data.");
return (0);
}
needfree = 1;
}
nc_handle = (void *) setnetconfig();
if (! nc_handle) {
msg("unable to get netconfig info.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
ep = srv->ep.ep_val;
epl = srv->ep.ep_len;
for (i = 0; i < sizeof (epcand)/sizeof (epcand[0]); i++) {
epcand[i] = 0;
nonipcand[i] = 0;
}
epc = 0;
nonip = 0;
while ((nc = getnetconfig(nc_handle)) != NULL) {
if ((nc->nc_flag & NC_VISIBLE) == 0)
continue;
for (i = 0; i < epl; i++) {
if (__nis_netconfig_matches_ep(nc, &(ep[i]))) {
if (MEP(ep[i], "udp") ||
MEP(ep[i], "udp6") ||
MEP(ep[i], "tcp") ||
MEP(ep[i], "tcp6")) {
epcand[epc++] = &(ep[i]);
} else {
nonipcand[nonip++] = &ep[i];
}
break;
}
}
}
(void) endnetconfig(nc_handle);
for (i = 0; i < nonip; i++) {
epcand[epc++] = nonipcand[i];
}
if (epc == 0) {
msg("no acceptable transport endpoints.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
} else {
if (*netid != 0) {
supplied.proto = *netid;
if (strcmp("udp", supplied.proto) &&
strcmp("udp6", supplied.proto) &&
strcmp("tcp", supplied.proto) &&
strcmp("tcp6", supplied.proto)) {
nonip = 1;
} else {
nonip = 0;
}
} else {
supplied.proto = (strchr(*uaddr, ':') != 0) ?
"udp6" : "udp";
nonip = 0;
}
supplied.uaddr = *uaddr;
supplied.family = (strchr(*uaddr, ':') != 0) ?
"inet6" : "inet";
epcand[0] = &supplied;
epc = 1;
nonip = 0;
}
nc = 0;
clnt = 0;
status = RPC_FAILED;
for (i = 0; i < epc; i++) {
if (nc != 0)
freenetconfigent(nc);
nc = getnetconfigent(epcand[i]->proto);
if (nc == 0) {
msg("unable to locate netconfig info for netid.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
useua = epcand[i]->uaddr;
useid = epcand[i]->proto;
if (strcasecmp(nc->nc_protofmly, NC_INET) == 0) {
(void) sscanf(useua,
"%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
(void) sprintf(ipuaddr, "%d.%d.%d.%d.0.111",
a1, a2, a3, a4);
useua = &ipuaddr[0];
} else if (strcasecmp(nc->nc_protofmly, NC_INET6) == 0) {
size_t len;
char *port = ".0.111";
if (strlen(useua) >= sizeof (ipuaddr)) {
freenetconfigent(nc);
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
(void) strcpy(ipuaddr, useua);
if ((dot = strrchr(ipuaddr, '.')) != 0) {
*dot = '\0';
if ((dot = strrchr(ipuaddr, '.')) != 0)
*dot = '\0';
}
if (dot == 0 ||
(len = strlen(ipuaddr))+strlen(port) >=
sizeof (ipuaddr)) {
freenetconfigent(nc);
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
(void) strcat(ipuaddr + len, port);
useua = ipuaddr;
}
if (clnt != 0)
clnt_destroy(clnt);
clnt = __nis_clnt_create(RPC_ANYFD, nc, useua, 0, 0, RPCBPROG,
RPCBVERS, 0, 0);
if (! clnt)
continue;
tv.tv_sec = 5;
tv.tv_usec = 0;
time_valid = 0;
status = clnt_call(clnt, RPCBPROC_GETTIME, xdr_void, NULL,
xdr_u_int, (char *)&thetime, tv);
if (status == RPC_SUCCESS)
break;
}
if (status == RPC_SUCCESS) {
time_valid = 1;
} else if (clnt == 0) {
msg("unable to create client handle to rpcbind.");
freenetconfigent(nc);
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
} else {
for (i = 0; i < epc-nonip; i++) {
if (nc != 0)
freenetconfigent(nc);
nc = getnetconfigent(epcand[i]->proto);
if (nc == 0) {
msg("no netconfig info for netid.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
useua = epcand[i]->uaddr;
useid = epcand[i]->proto;
if (strcasecmp(nc->nc_protofmly, NC_INET) == 0) {
(void) sscanf(useua,
"%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
(void) sprintf(ut, "%d.%d.%d.%d.0.37",
a1, a2, a3, a4);
} else if (strcasecmp(nc->nc_protofmly, NC_INET6) ==
0) {
size_t len;
char *port = ".0.37";
if (strlen(useua) >= sizeof (ut)) {
goto error;
}
(void) strcpy(ut, useua);
if ((dot = strrchr(ut, '.')) != 0) {
*dot = '\0';
if ((dot = strrchr(ut, '.')) != 0)
*dot = '\0';
}
if (dot == 0) {
goto error;
}
if ((len = strlen(ut))+strlen(port) >=
sizeof (ut)) {
goto error;
}
(void) strcat(ut + len, port);
}
addr = uaddr2taddr(nc, ut);
if (! addr) {
msg("timeservice uaddr to taddr failed.");
goto error;
}
rtime_fd = t_open(nc->nc_device, O_RDWR, NULL);
if (rtime_fd == -1) {
msg("unable to open fd to network.");
goto error;
}
if (t_bind(rtime_fd, NULL, NULL) < 0) {
msg("unable to bind an endpoint to fd.");
goto error;
}
if (nc->nc_semantics == NC_TPI_CLTS) {
struct t_unitdata tu_data;
struct pollfd pfd;
int res;
tu_data.addr = *addr;
tu_data.udata.buf = (char *)&thetime;
tu_data.udata.len = (uint_t)sizeof (thetime);
tu_data.udata.maxlen = tu_data.udata.len;
tu_data.opt.len = 0;
tu_data.opt.maxlen = 0;
if (t_sndudata(rtime_fd, &tu_data) == -1) {
msg("udp : t_sndudata failed.");
goto error;
}
pfd.fd = rtime_fd;
pfd.events =
POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND;
do {
res = poll(&pfd, 1, 10000);
} while (res < 0);
if ((res <= 0) || (pfd.revents & POLLNVAL))
goto error;
if (t_rcvudata(rtime_fd, &tu_data, &flag) <
0) {
msg("t_rvcdata failed on udp trpt.");
goto error;
}
time_valid = 1;
} else {
struct t_call sndcall;
sndcall.addr = *addr;
sndcall.opt.len = sndcall.opt.maxlen = 0;
sndcall.udata.len = sndcall.udata.maxlen = 0;
oldsig = (void (*)())signal(SIGALRM,
alarm_hndler);
saw_alarm = 0;
(void) alarm(20);
if (t_connect(rtime_fd, &sndcall, NULL) ==
-1) {
msg("connect tcp endpoint failedd.");
goto error;
}
if (saw_alarm) {
msg("alarm caught it; unreachable.");
goto error;
}
if (t_rcv(rtime_fd, (char *)&thetime,
(uint_t)sizeof (thetime), &flag) !=
(uint_t)sizeof (thetime)) {
if (saw_alarm) {
msg("timed out TCP call.");
} else {
msg("wrong size results");
}
goto error;
}
time_valid = 1;
}
if (time_valid) {
thetime = ntohl(thetime);
thetime = thetime - TOFFSET;
} else
thetime = 0;
}
}
error:
if (addr)
netdir_free((char *)(addr), ND_ADDR);
if (rtime_fd != -1)
(void) t_close(rtime_fd);
if (clnt)
clnt_destroy(clnt);
if (nc)
freenetconfigent(nc);
if (oldsig) {
(void) alarm(0);
(void) signal(SIGALRM, oldsig);
}
if (time_valid) {
if (! *netid) {
*netid = strdup(useid);
if (! *netid) {
msg("__rpc_get_time_offset: strdup failed.");
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
*uaddr = strdup(useua);
if (! *uaddr) {
msg("__rpc_get_time_offset: strdup failed.");
if (*netid)
free(*netid);
if (needfree)
free_eps(teps, tsrv.ep.ep_len);
return (0);
}
}
(void) gettimeofday(&tv, 0);
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);
}