/*
* 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
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This module contains the private function __rpc_get_time_offset()
* which will return the difference in seconds between the local system's
* notion of time and a remote server's notion of time. This must be
* possible without calling any functions that may invoke the name
* service. (netdir_getbyxxx, getXbyY, etc). The function is used in the
* synchronize call of the authdes code to synchronize clocks between
* NIS+ clients and their servers.
*
* Note to minimize the amount of duplicate code, portions of the
* synchronize() function were folded into this code, and the synchronize
* call becomes simply a wrapper around this function. Further, if this
* function is called with a timehost it *DOES* recurse to the name
* server so don't use it in that mode if you are doing name service code.
*
* Side effects :
* When called a client handle to a RPCBIND process is created
* and destroyed. Two strings "netid" and "uaddr" are malloc'd
* and returned. The SIGALRM processing is modified only if
* needed to deal with TCP connections.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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>
#ifdef TESTING
/* #define msg(x) syslog(LOG_ERR, "%s", x) */
#else
#define msg(x)
#endif
static int saw_alarm = 0;
/* ARGSUSED */
static void
alarm_hndler(int s)
{
saw_alarm = 1;
}
/*
* The internet time server defines the epoch to be Jan 1, 1900
* whereas UNIX defines it to be Jan 1, 1970. To adjust the result
* from internet time-service time, into UNIX time we subtract the
* following offset :
*/
/*
* free_eps()
*
* Free the strings that were strduped into the eps structure.
*/
static void
{
int i;
for (i = 0; i < num; i++) {
}
}
/*
* get_server()
*
* This function constructs a nis_server structure description for the
* indicated hostname.
*/
static nis_server *
{
int num_ep = 0, i;
void *nch;
if (! host)
return (NULL);
nch = setnetconfig();
continue;
i++, num_ep++) {
}
}
}
(void) endnetconfig(nch);
return (srv);
}
/*
* __rpc_get_time_offset()
*
* This function uses a nis_server structure to contact the a remote
* machine (as named in that structure) and returns the offset in time
* between that machine and this one. This offset is returned in seconds
* and may be positive or negative.
*
* The first time through, a lot of fiddling is done with the netconfig
* stuff to find a suitable transport. The function is very aggressive
* about choosing UDP or at worst TCP if it can. This is because
* those transports support both the RCPBIND call and the internet
* time service.
*
* Once through, *uaddr is set to the universal address of
* the machine and *netid is set to the local netid for the transport
* that uaddr goes with. On the second call, the netconfig stuff
* structure and to then contact the machine for the time.
*
* td = "server" - "client"
*/
int
{
int needfree = 0;
/*
* First check to see if we need to find and address for this
* server.
*/
msg("both timehost and srv pointer used!");
return (0);
}
if (! srv) {
if (! srv) {
msg("unable to contruct server data.");
return (0);
}
}
nc_handle = (void *) setnetconfig();
if (! nc_handle) {
msg("unable to get netconfig info.");
if (needfree)
return (0);
}
epcand[i] = 0;
nonipcand[i] = 0;
}
epc = 0;
nonip = 0;
/*
* Build the list of endpoint candidates. We prefer transports
* ordering among the IP transports.
*
* Note: We assume that the endpoint 'proto' field contains
* the netid of the transport.
*/
/* Is it a visible transport ? */
continue;
/* Check against the end points */
for (i = 0; i < epl; i++) {
} else {
}
break;
}
}
}
(void) endnetconfig(nc_handle);
/*
* epcand[] now contains the candidate transports. If there
* were non-IP transports as well, add them to the end of the
* candidate list.
*/
for (i = 0; i < nonip; i++) {
}
if (epc == 0) {
msg("no acceptable transport endpoints.");
if (needfree)
return (0);
}
} else {
/* Caller supplied a uaddr. Fake an endpoint. */
if (*netid != 0) {
/* Is it one of the known IP transports ? */
/* No, it's not */
nonip = 1;
} else {
nonip = 0;
}
} else {
"udp6" : "udp";
nonip = 0;
}
"inet6" : "inet";
epc = 1;
nonip = 0;
}
nc = 0;
clnt = 0;
/*
* Loop over the endpoint candidates. Defer error reporting (except
* for the netconfig entry) until we've looked at all candidates.
*/
for (i = 0; i < epc; i++) {
if (nc != 0)
if (nc == 0) {
msg("unable to locate netconfig info for netid.");
if (needfree)
return (0);
}
/*
* Add the appropriate port number to the uaddr
*/
if (needfree)
return (0);
}
/* get the IPv6 address out of the uaddr */
*dot = '\0';
*dot = '\0';
}
if (dot == 0 ||
sizeof (ipuaddr)) {
if (needfree)
return (0);
}
/* now put in 0.111 */
}
/*
* Create the client handle to rpcbind. Note we always try
* version 3 since that is the earliest version that supports
* the RPCB_GETTIME call. Also it is the version that comes
* we could consider trying the rtime call first.
*/
if (clnt != 0)
RPCBVERS, 0, 0);
if (! clnt)
continue;
time_valid = 0;
/*
* The only error we check for is anything but success. In
* fact we could have seen PROGMISMATCH if talking to a 4.1
* machine (pmap v2) or TIMEDOUT if the net was busy.
*/
if (status == RPC_SUCCESS)
break;
}
if (status == RPC_SUCCESS) {
time_valid = 1;
} else if (clnt == 0) {
msg("unable to create client handle to rpcbind.");
if (needfree)
return (0);
} else {
/*
* Try the timeservice port. This presumably only exists
* for IP transports, so we ignore the non-IP ones.
*/
/*
* Convert PMAP address into timeservice address
* We take advantage of the fact that we "know" what
* a universal address looks like for inet transports.
*
* We also know that the internet timeservice is always
* listening on port 37.
*/
if (nc != 0)
if (nc == 0) {
msg("no netconfig info for netid.");
if (needfree)
return (0);
}
0) {
goto error;
}
/* get the IPv6 address out of the uaddr */
*dot = '\0';
*dot = '\0';
}
if (dot == 0) {
goto error;
}
sizeof (ut)) {
goto error;
}
}
if (! addr) {
msg("timeservice uaddr to taddr failed.");
goto error;
}
if (rtime_fd == -1) {
msg("unable to open fd to network.");
goto error;
}
msg("unable to bind an endpoint to fd.");
goto error;
}
/*
* Now depending on whether or not we're talking to
* UDP we set a timeout or not.
*/
int res;
msg("udp : t_sndudata failed.");
goto error;
}
do {
} while (res < 0);
goto error;
0) {
msg("t_rvcdata failed on udp trpt.");
goto error;
}
time_valid = 1;
} else {
saw_alarm = 0; /* global tracking the alarm */
-1) {
msg("connect tcp endpoint failedd.");
goto error;
}
if (saw_alarm) {
msg("alarm caught it; unreachable.");
goto error;
}
if (saw_alarm) {
/*EMPTY*/
msg("timed out TCP call.");
} else {
/*EMPTY*/
msg("wrong size results");
}
goto error;
}
time_valid = 1;
}
if (time_valid) {
/* adjust to UNIX time */
} else
thetime = 0;
}
}
/*
* clean up our allocated data structures.
*/
if (addr)
if (rtime_fd != -1)
if (clnt)
if (nc)
if (oldsig) {
(void) alarm(0); /* reset that alarm if its outstanding */
}
/*
* note, don't free uaddr strings until after we've made a
* copy of them.
*/
if (time_valid) {
if (! *netid) {
if (! *netid) {
msg("__rpc_get_time_offset: strdup failed.");
if (needfree)
return (0);
}
if (! *uaddr) {
msg("__rpc_get_time_offset: strdup failed.");
if (*netid)
if (needfree)
return (0);
}
}
(void) gettimeofday(&tv, 0);
/* Round to the nearest second */
} else {
/*EMPTY*/
msg("unable to get the server's time.");
}
if (needfree)
return (time_valid);
}