/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2014 Gary Mills
* Copyright 2001 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
/*
* Miscellaneous support routines for kernel implentation of AUTH_DES
*/
/*
* rtime - get time from remote machine
*
* sets time, obtaining value from host
* on the udp/time socket. Since timeserver returns
* with time of day in seconds since Jan 1, 1900, must
* subtract 86400(365*70 + 17) to get time
* since Jan 1, 1970, which is what get/settimeofday
* uses.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/cred.h>
#include <sys/utsname.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/systeminfo.h>
#include <rpc/rpcb_prot.h>
#include <sys/cmn_err.h>
#define TOFFSET ((uint32_t)86400 * (365 * 70 + (70 / 4)))
#define WRITTEN ((uint32_t)86400 * (365 * 86 + (86 / 4)))
#define NC_INET "inet" /* XXX */
int
rtime(struct knetconfig *synconfig, struct netbuf *addrp, int calltype,
struct timeval *timep, struct timeval *wait)
{
int error;
int timo;
time_t thetime;
int32_t srvtime;
uint32_t dummy;
struct t_kunitdata *unitdata;
struct t_call *server;
TIUSER *tiptr;
int type;
int uderr;
int i;
int retries;
mblk_t *mp;
mblk_t *mp2;
retries = 5;
if (calltype == 0) {
again:
RPCLOG0(8, "rtime: using old method\n");
if ((error = t_kopen(NULL, synconfig->knc_rdev,
FREAD|FWRITE, &tiptr, CRED())) != 0) {
RPCLOG(1, "rtime: t_kopen %d\n", error);
return (-1);
}
if ((error = t_kbind(tiptr, NULL, NULL)) != 0) {
(void) t_kclose(tiptr, 1);
RPCLOG(1, "rtime: t_kbind %d\n", error);
return (-1);
}
if (synconfig->knc_semantics == NC_TPI_CLTS) {
if ((error = t_kalloc(tiptr, T_UNITDATA,
T_UDATA|T_ADDR, (char **)&unitdata)) != 0) {
RPCLOG(1, "rtime: t_kalloc %d\n", error);
(void) t_kclose(tiptr, 1);
return (-1);
}
unitdata->addr.len = addrp->len;
bcopy(addrp->buf, unitdata->addr.buf,
unitdata->addr.len);
dummy = 0;
unitdata->udata.buf = (caddr_t)&dummy;
unitdata->udata.len = sizeof (dummy);
if ((error = t_ksndudata(tiptr, unitdata, NULL)) !=
0) {
RPCLOG(1, "rtime: t_ksndudata %d\n", error);
(void) t_kfree(tiptr, (char *)unitdata,
T_UNITDATA);
(void) t_kclose(tiptr, 1);
return (-1);
}
timo = TIMEVAL_TO_TICK(wait);
RPCLOG(8, "rtime: timo %x\n", timo);
if ((error = t_kspoll(tiptr, timo, READWAIT,
&type)) != 0) {
RPCLOG(1, "rtime: t_kspoll %d\n", error);
(void) t_kfree(tiptr, (char *)unitdata,
T_UNITDATA);
(void) t_kclose(tiptr, 1);
return (-1);
}
if (type == 0) {
RPCLOG0(1, "rtime: t_kspoll timed out\n");
(void) t_kfree(tiptr, (char *)unitdata,
T_UNITDATA);
(void) t_kclose(tiptr, 1);
return (-1);
}
error = t_krcvudata(tiptr, unitdata, &type, &uderr);
if (error != 0) {
RPCLOG(1, "rtime: t_krcvudata %d\n", error);
(void) t_kfree(tiptr, (char *)unitdata,
T_UNITDATA);
(void) t_kclose(tiptr, 1);
if (error == EBADMSG && retries-- > 0)
goto again;
return (-1);
}
if (type == T_UDERR) {
if (bcmp(addrp->buf, unitdata->addr.buf,
unitdata->addr.len) != 0) {
/*
* Response comes from some other
* destination:
* ignore it since it's not related to the
* request we just sent out.
*/
(void) t_kfree(tiptr, (char *)unitdata,
T_UNITDATA);
(void) t_kclose(tiptr, 1);
goto again;
}
}
if (type != T_DATA) {
RPCLOG(1,
"rtime: t_krcvudata returned type %d\n",
type);
(void) t_kfree(tiptr, (char *)unitdata,
T_UNITDATA);
(void) t_kclose(tiptr, 1);
if (retries-- == 0)
return (-1);
goto again;
}
if (unitdata->udata.len < sizeof (uint32_t)) {
RPCLOG(1, "rtime: bad rcvd length %d\n",
unitdata->udata.len);
(void) t_kfree(tiptr, (char *)unitdata,
T_UNITDATA);
(void) t_kclose(tiptr, 1);
if (retries-- == 0)
return (-1);
goto again;
}
thetime = (time_t)ntohl(
/* LINTED pointer alignment */
*(uint32_t *)unitdata->udata.buf);
(void) t_kfree(tiptr, (char *)unitdata, T_UNITDATA);
} else {
if ((error = t_kalloc(tiptr, T_CALL, T_ADDR,
(char **)&server)) != 0) {
RPCLOG(1, "rtime: t_kalloc %d\n", error);
(void) t_kclose(tiptr, 1);
return (-1);
}
server->addr.len = addrp->len;
bcopy(addrp->buf, server->addr.buf, server->addr.len);
if ((error = t_kconnect(tiptr, server, NULL)) != 0) {
RPCLOG(1, "rtime: t_kconnect %d\n", error);
(void) t_kfree(tiptr, (char *)server, T_CALL);
(void) t_kclose(tiptr, 1);
return (-1);
}
(void) t_kfree(tiptr, (char *)server, T_CALL);
timo = TIMEVAL_TO_TICK(wait);
RPCLOG(8, "rtime: timo %x\n", timo);
i = 0;
dummy = 0;
/* now read up to 4 bytes from the TIME server */
while (i < sizeof (dummy)) {
error = t_kspoll(tiptr, timo, READWAIT, &type);
if (error != 0) {
RPCLOG(1, "rtime: t_kspoll %d\n",
error);
(void) t_kclose(tiptr, 1);
return (-1);
}
if (type == 0) {
RPCLOG0(1,
"rtime: t_kspoll timed out\n");
(void) t_kclose(tiptr, 1);
return (-1);
}
error = tli_recv(tiptr, &mp,
tiptr->fp->f_flag);
if (error != 0) {
RPCLOG(1, "rtime: tli_recv %d\n",
error);
(void) t_kclose(tiptr, 1);
return (-1);
}
if (mp->b_datap->db_type != M_DATA) {
RPCLOG(1, "rtime: wrong msg type %d\n",
mp->b_datap->db_type);
RPCLOG(1,
"rtime: wrong msg type: read %d"
" bytes\n", i);
(void) t_kclose(tiptr, 1);
freemsg(mp);
return (-1);
}
mp2 = mp;
/*
* The outer loop iterates until we reach the
* end of the mblk chain.
*/
while (mp2 != NULL) {
/*
* The inner loop iterates until
* we've gotten 4 bytes or until
* the mblk is exhausted.
*/
while (i < sizeof (dummy) &&
mp2->b_rptr < mp2->b_wptr) {
i++;
/*
* We avoid big-endian/little-endian
* issues by serializing the result
* one byte at a time.
*/
dummy <<= 8;
dummy += ((*mp2->b_rptr) &
0xFF);
mp2->b_rptr++;
}
mp2 = mp2->b_cont;
}
freemsg(mp);
}
thetime = (time_t)dummy;
}
(void) t_kclose(tiptr, 1);
} else {
CLIENT *client;
struct timeval timout;
RPCLOG0(8, "rtime: using new method\n");
new_again:
/*
* We talk to rpcbind.
*/
error = clnt_tli_kcreate(synconfig, addrp, (rpcprog_t)RPCBPROG,
(rpcvers_t)RPCBVERS, 0, retries, CRED(), &client);
if (error != 0) {
RPCLOG(1,
"rtime: clnt_tli_kcreate returned %d\n", error);
return (-1);
}
timout.tv_sec = 60;
timout.tv_usec = 0;
error = clnt_call(client, RPCBPROC_GETTIME, (xdrproc_t)xdr_void,
NULL, (xdrproc_t)xdr_u_int,
(caddr_t)&srvtime, timout);
thetime = srvtime;
auth_destroy(client->cl_auth);
clnt_destroy(client);
if (error == RPC_UDERROR) {
if (retries-- > 0)
goto new_again;
}
if (error != RPC_SUCCESS) {
RPCLOG(1, "rtime: time sync clnt_call returned %d\n",
error);
error = EIO;
return (-1);
}
}
if (calltype != 0)
thetime += TOFFSET;
RPCLOG(8, "rtime: thetime = %lx\n", thetime);
if (thetime < WRITTEN) {
RPCLOG(1, "rtime: time returned is too far in past %lx",
thetime);
RPCLOG(1, "rtime: WRITTEN %x", WRITTEN);
return (-1);
}
thetime -= TOFFSET;
timep->tv_sec = thetime;
RPCLOG(8, "rtime: timep->tv_sec = %lx\n", timep->tv_sec);
RPCLOG(8, "rtime: machine time = %lx\n", gethrestime_sec());
timep->tv_usec = 0;
RPCLOG0(8, "rtime: returning success\n");
return (0);
}
/*
* What is my network name?
* WARNING: this gets the network name in sun unix format.
* Other operating systems (non-unix) are free to put something else
* here.
*
* Return 0 on success
* Return RPC errors (non-zero values) if failed.
*/
enum clnt_stat
kgetnetname(char *netname)
{
return (key_getnetname(netname, CRED()));
}