Beispiel #1
0
/*
 * get_server()
 *
 * This function constructs a nis_server structure description for the
 * indicated hostname.
 *
 * NOTE: There is a chance we may end up recursing here due to the
 * fact that gethostbyname() could do an NIS search. Ideally, the
 * NIS+ server will call __rpc_get_time_offset() with the nis_server
 * structure already populated.
 */
static nis_server *
get_server(struct sockaddr_in *sin,
           char  *host, /* name of the time host */
           nis_server *srv,  /* nis_server struct to use. */
           endpoint eps[], /* array of endpoints */
           int  maxep  /* max array size */)
{
    char   hname[256];
    int   num_ep = 0, i;
    struct hostent  *he;
    struct hostent  dummy;
    char   *ptr[2];

    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;
    }

    /*
     * This is lame. We go around once for TCP, then again
     * for UDP.
     */
    for (i = 0; (he->h_addr_list[i] != NULL) && (num_ep < maxep);
         i++, 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));
        eps[num_ep].uaddr = rpc_strdup(hname);
        eps[num_ep].family = rpc_strdup("inet");
        eps[num_ep].proto =  rpc_strdup("tcp");
    }

    for (i = 0; (he->h_addr_list[i] != NULL) && (num_ep < maxep);
         i++, 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));
        eps[num_ep].uaddr = rpc_strdup(hname);
        eps[num_ep].family = rpc_strdup("inet");
        eps[num_ep].proto =  rpc_strdup("udp");
    }

    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);
}
Beispiel #2
0
/*
 * __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
 * is skipped and the uaddr/netid pair are used to fetch the netconfig
 * structure and to then contact the machine for the time.
 *
 * td = "server" - "client"
 */
int
__rpc_get_time_offset(struct timeval *td, /* Time difference */
                      nis_server *srv,  /* NIS Server description */
                      char  *thost,  /* if no server, this is the timehost */
                      char  **uaddr, /* known universal address */
                      struct sockaddr_in *netid /* known network identifier */)
{
    CLIENT   *clnt;   /* Client handle  */
    AUTH     *auth;
    endpoint  *ep,  /* useful endpoints */
        *useep = NULL; /* endpoint of xp */
    char   *useua = NULL; /* uaddr of selected xp */
    int   epl, i;  /* counters  */
    enum clnt_stat  status;  /* result of clnt_call */
    u_long   thetime, delta;
    int   needfree = 0;
    struct timeval tv;
    struct timespec ts;
    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)() = NULL; /* old alarm handler */
    struct sockaddr_in sin;
    int   s = RPC_ANYSOCK;
    socklen_t len;
    int   type = 0;
    div_t q;

    td->tv_sec = 0;
    td->tv_usec = 0;

    /*
     * First check to see if we need to find and address for this
     * server.
     */
    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; /* need to free data in endpoints */
        }

        ep = srv->ep.ep_val;
        epl = srv->ep.ep_len;

        /* Identify the TCP and UDP endpoints */
        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;
        }

        /* Check to see if it is UDP or TCP */
        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);
        }
    }

    /*
     * Create a sockaddr from the uaddr.
     */
    if (*uaddr != NULL)
        useua = *uaddr;

    /* Fixup test for NIS+ */
    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);
    }

    /*
     * 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
     * standard with SVR4. Since most everyone supports TCP/IP
     * we could consider trying the rtime call first.
     */
    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);
    }

    auth = authnone_ncreate(); /* idempotent */

    tv.tv_sec = 5;
    tv.tv_usec = 0;
    time_valid = 0;
    status = clnt_call(clnt, auth, RPCBPROC_GETTIME, (xdrproc_t)xdr_void, NULL,
                       (xdrproc_t)xdr_u_long, &thetime, tv);
    /*
     * 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)
        time_valid = 1;
    else {
        int save;

        /* Blow away possible stale CLNT handle. */
        if (clnt != NULL) {
            clnt_destroy(clnt);
            clnt = NULL;
        }

        /*
         * Convert PMAP address into timeservice address
         * We take advantage of the fact that we "know" what
         * the universal address looks like for inet transports.
         *
         * We also know that the internet timeservice is always
         * listening on port 37.
         */
        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;
        }

        /*
         * Now depending on whether or not we're talking to
         * UDP we set a timeout or not.
         */
        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 (*)())signal(SIGALRM, alarm_hndler);
            saw_alarm = 0; /* global tracking the alarm */
            alarm(20); /* only wait 20 seconds */
            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; /* adjust to UNIX time */
        } else
            thetime = 0;
    }

    (void) clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
    tv.tv_sec = ts.tv_sec;
    q = div(ts.tv_nsec, 1000);
    tv.tv_usec = q.quot;

error:
    /*
     * clean up our allocated data structures.
     */

    if (s != RPC_ANYSOCK)
        (void)close(s);

    if (clnt != NULL)
        clnt_destroy(clnt);

    alarm(0); /* reset that alarm if its outstanding */
    if (oldsig) {
        signal(SIGALRM, oldsig);
    }

    /*
     * note, don't free uaddr strings until after we've made a
     * copy of them.
     */
    if (time_valid) {
        if (*uaddr == NULL)
            *uaddr = rpc_strdup(useua);

        /* Round to the nearest second */
        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);
}
Beispiel #3
0
int
rpc_reg(rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum,
	char *(*progname) (char *), xdrproc_t inproc, xdrproc_t outproc,
	char *nettype)
{
	struct netconfig *nconf;
	int done = false;
	void *handle;
	extern mutex_t proglst_lock;

	if (procnum == NULLPROC) {
		__warnx(TIRPC_DEBUG_FLAG_SVC,
			"%s can't reassign procedure number %u", rpc_reg_msg,
			NULLPROC);
		return (-1);
	}

	if (nettype == NULL)
		nettype = "netpath";	/* The default behavior */
	handle = __rpc_setconf(nettype);
	if (!handle) {
		__warnx(TIRPC_DEBUG_FLAG_SVC, rpc_reg_err, rpc_reg_msg,
			__reg_err1);
		return (-1);
	}
	/* VARIABLES PROTECTED BY proglst_lock: proglst */
	mutex_lock(&proglst_lock);
	while ((nconf = __rpc_getconf(handle)) != NULL) {
		struct proglst *pl;
		SVCXPRT *svcxprt;
		int madenow;
		u_int recvsz;
		char *xdrbuf;
		char *netid;

		madenow = false;
		svcxprt = NULL;
		recvsz = 0;
		xdrbuf = netid = NULL;
		for (pl = proglst; pl; pl = pl->p_nxt) {
			if (strcmp(pl->p_netid, nconf->nc_netid) == 0) {
				svcxprt = pl->p_transp;
				xdrbuf = pl->p_xdrbuf;
				recvsz = pl->p_recvsz;
				netid = pl->p_netid;
				break;
			}
		}

		if (svcxprt == NULL) {
			struct __rpc_sockinfo si;

			svcxprt = svc_tli_ncreate(RPC_ANYFD, nconf, NULL, 0, 0);
			if (svcxprt == NULL)
				continue;
			if (!__rpc_fd2sockinfo(svcxprt->xp_fd, &si)) {
				__warnx(TIRPC_DEBUG_FLAG_SVC, rpc_reg_err,
					rpc_reg_msg, __reg_err2);
				SVC_DESTROY(svcxprt);
				continue;
			}
			recvsz = __rpc_get_t_size(si.si_af, si.si_proto, 0);
			if (recvsz == 0) {
				__warnx(TIRPC_DEBUG_FLAG_SVC, rpc_reg_err,
					rpc_reg_msg, __reg_err3);
				SVC_DESTROY(svcxprt);
				continue;
			}
			xdrbuf = mem_alloc((unsigned)recvsz);
			if (xdrbuf)
				netid = rpc_strdup(nconf->nc_netid);
			if ((!xdrbuf) ||
			    (!netid)) {
				if (xdrbuf)
					mem_free(xdrbuf, (unsigned)recvsz);
				__warnx(TIRPC_DEBUG_FLAG_SVC, rpc_reg_err,
					rpc_reg_msg, __no_mem_str);
				SVC_DESTROY(svcxprt);
				break;
			}
			madenow = true;
		}
		/*
		 * Check if this (program, version, netid) had already been
		 * registered.  The check may save a few RPC calls to rpcbind
		 */
		for (pl = proglst; pl; pl = pl->p_nxt)
			if ((pl->p_prognum == prognum)
			    && (pl->p_versnum == versnum)
			    && (strcmp(pl->p_netid, netid) == 0))
				break;
		if (pl == NULL) {	/* Not yet */
			(void)rpcb_unset(prognum, versnum, nconf);
		} else {
			/* so that svc_reg does not call rpcb_set() */
			nconf = NULL;
		}

		if (!svc_reg(svcxprt, prognum, versnum, universal, nconf)) {
			__warnx(TIRPC_DEBUG_FLAG_SVC,
				"%s couldn't register prog %u vers %u for %s",
				rpc_reg_msg, (unsigned)prognum,
				(unsigned)versnum, netid);
			if (madenow) {
				SVC_DESTROY(svcxprt);
				mem_free(xdrbuf, 0);
				mem_free(netid, 0);
			}
			continue;
		}

		pl = mem_alloc(sizeof(struct proglst));
		if (pl == NULL) {
			__warnx(TIRPC_DEBUG_FLAG_SVC, rpc_reg_err, rpc_reg_msg,
				__no_mem_str);
			if (madenow) {
				SVC_DESTROY(svcxprt);
				mem_free(xdrbuf, 0);
				mem_free(netid, 0);
			}
			break;
		}
		pl->p_progname = progname;
		pl->p_prognum = prognum;
		pl->p_versnum = versnum;
		pl->p_procnum = procnum;
		pl->p_inproc = inproc;
		pl->p_outproc = outproc;
		pl->p_transp = svcxprt;
		pl->p_xdrbuf = xdrbuf;
		pl->p_recvsz = recvsz;
		pl->p_netid = netid;
		pl->p_nxt = proglst;
		proglst = pl;
		done = true;
	}
	__rpc_endconf(handle);
	mutex_unlock(&proglst_lock);

	if (done == false) {
		__warnx(TIRPC_DEBUG_FLAG_SVC,
			"%s cant find suitable transport for %s", rpc_reg_msg,
			nettype);
		return (-1);
	}
	return (0);
}