예제 #1
0
파일: pmap_clnt.c 프로젝트: aosm/Kerberos
/*
 * Remove the mapping between program,version and port.
 * Calls the pmap service remotely to do the un-mapping.
 */
bool_t
pmap_unset(
	rpcprog_t program,
	rpcvers_t version)
{
	struct sockaddr_in myaddress;
	int socket = -1;
	register CLIENT *client;
	struct pmap parms;
	bool_t rslt;

	get_myaddress(&myaddress);
	client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
	    timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
	if (client == (CLIENT *)NULL)
		return (FALSE);
	parms.pm_prog = program;
	parms.pm_vers = version;
	parms.pm_port = parms.pm_prot = 0;
	CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt,
	    tottimeout);
	CLNT_DESTROY(client);
	(void)close(socket);
	return (rslt);
}
예제 #2
0
/*
 * Get a copy of the current port maps.
 * Calls the pmap service remotely to do get the maps.
 */
struct pmaplist *
pmap_getmaps(struct sockaddr_in *address)
{
	struct pmaplist *head = NULL;
	int sock = -1;
	struct timeval minutetimeout;
	CLIENT *client;

	assert(address != NULL);

	minutetimeout.tv_sec = 60;
	minutetimeout.tv_usec = 0;
	address->sin_port = htons(PMAPPORT);
	client = clnttcp_create(address, PMAPPROG,
	    PMAPVERS, &sock, 50, 500);
	if (client != NULL) {
		if (CLNT_CALL(client, (rpcproc_t)PMAPPROC_DUMP,
		    (xdrproc_t)xdr_void, NULL,
		    (xdrproc_t)xdr_pmaplist, &head, minutetimeout) !=
		    RPC_SUCCESS) {
			clnt_perror(client, "pmap_getmaps rpc problem");
		}
		CLNT_DESTROY(client);
	}
	address->sin_port = 0;
	return (head);
}
예제 #3
0
/*
 * Remove the mapping between program,version and port.
 * Calls the pmap service remotely to do the un-mapping.
 */
bool_t
pmap_unset(u_long program, u_long version)
{
	struct sockaddr_in myaddress;
	int sock = -1;
	CLIENT *client;
	struct pmap parms;
	bool_t rslt;

	if (get_myaddress(&myaddress) != 0)
		return (FALSE);
	myaddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
	    timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
	if (client == NULL)
		return (FALSE);
	parms.pm_prog = program;
	parms.pm_vers = version;
	parms.pm_port = parms.pm_prot = 0;
	CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt,
	    tottimeout);
	CLNT_DESTROY(client);
	if (sock != -1)
		(void)close(sock);
	return (rslt);
}
예제 #4
0
/*
 * pmapper remote-call-service interface.
 * This routine is used to call the pmapper remote call service
 * which will look up a service program in the port maps, and then
 * remotely call that routine with the given parameters.  This allows
 * programs to do a lookup and call in one step.
*/
enum clnt_stat
pmap_rmtcall(struct sockaddr_in *addr, u_long prog, u_long vers, u_long proc,
    xdrproc_t xdrargs, caddr_t argsp, xdrproc_t xdrres, caddr_t resp,
    struct timeval tout, u_long *port_ptr)
{
	int sock = -1;
	CLIENT *client;
	struct rmtcallargs a;
	struct rmtcallres r;
	enum clnt_stat stat;

	addr->sin_port = htons(PMAPPORT);
	client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &sock);
	if (client != NULL) {
		a.prog = prog;
		a.vers = vers;
		a.proc = proc;
		a.args_ptr = argsp;
		a.xdr_args = xdrargs;
		r.port_ptr = port_ptr;
		r.results_ptr = resp;
		r.xdr_results = xdrres;
		stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a,
		    xdr_rmtcallres, &r, tout);
		CLNT_DESTROY(client);
	} else {
		stat = RPC_FAILED;
	}
	if (sock != -1)
		(void)close(sock);
	addr->sin_port = 0;
	return (stat);
}
예제 #5
0
/*
 * Find the mapped port for program,version.
 * Calls the pmap service remotely to do the lookup.
 * Returns 0 if no map exists.
 */
u_short
pmap_getport(
    struct sockaddr_in *address,
    rpcprog_t program,
    rpcvers_t version,
    rpcprot_t protocol)
{
    unsigned short port = 0;
    int sock = -1;
    register CLIENT *client;
    struct pmap parms;

    address->sin_port = htons(PMAPPORT);
    client = clntudp_bufcreate(address, PMAPPROG,
                               PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
    if (client != (CLIENT *)NULL) {
        parms.pm_prog = program;
        parms.pm_vers = version;
        parms.pm_prot = protocol;
        parms.pm_port = 0;  /* not needed or used */
        if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
                      xdr_u_short, &port, tottimeout) != RPC_SUCCESS) {
            rpc_createerr.cf_stat = RPC_PMAPFAILURE;
            clnt_geterr(client, &rpc_createerr.cf_error);
        } else if (port == 0) {
            rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
        }
        CLNT_DESTROY(client);
    }
    (void)close(sock);
    address->sin_port = 0;
    return (port);
}
예제 #6
0
/*
 * Remove the mapping between program,version and port.
 * Calls the pmap service remotely to do the un-mapping.
 */
bool_t
pmap_unset(
	unsigned long program,
	unsigned long version)
{
	struct sockaddr_in myaddress;
	int sockfd = -1;
	register CLIENT *client;
	struct pmap parms;
	bool_t rslt;

	get_myaddress(&myaddress);
	client = clnttcp_create(&myaddress, PMAPPROG, PMAPVERS,
		&sockfd, 0, 0);
	if (client == (CLIENT *)NULL)
		return (FALSE);
	parms.pm_prog = program;
	parms.pm_vers = version;
	parms.pm_port = parms.pm_prot = 0;
	if (CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt,
	    tottimeout) != RPC_SUCCESS) {
		clnt_perror(client, "pmap_unset: Cannot unregister service");
		rslt = FALSE;
	}
	CLNT_DESTROY(client);
	return (rslt);
}
예제 #7
0
/*
 * Get a copy of the current port maps.
 * Calls the pmap service remotely to do get the maps.
 */
struct pmaplist *
pmap_getmaps (struct sockaddr_in *address)
{
    struct pmaplist *head = (struct pmaplist *) NULL;
    int _socket = -1;
    struct timeval minutetimeout;
    CLIENT *client;

    minutetimeout.tv_sec = 60;
    minutetimeout.tv_usec = 0;
    address->sin_port = htons (PMAPPORT);

    /* Don't need a reserved port to get ports from the portmapper.  */
    client = clnttcp_create (address, PMAPPROG,
                             PMAPVERS, &_socket, 50, 500);
    if (client != (CLIENT *) NULL)
    {
        if (CLNT_CALL (client, PMAPPROC_DUMP, (xdrproc_t)xdr_void, NULL,
                       (xdrproc_t)xdr_pmaplist, (caddr_t)&head,
                       minutetimeout) != RPC_SUCCESS)
        {
            clnt_perror (client, _("pmap_getmaps rpc problem"));
        }
        CLNT_DESTROY (client);
    }
    /* (void)close(_socket); CLNT_DESTROY already closed it */
    address->sin_port = 0;
    return head;
}
예제 #8
0
/*
 * Find the mapped port for program,version.
 * Calls the pmap service remotely to do the lookup.
 * Returns 0 if no map exists.
 */
u_short
pmap_getport(struct sockaddr_in *address, u_long program, u_long version,
    u_int protocol)
{
	u_short port = 0;
	int sock = -1;
	CLIENT *client;
	struct pmap parms;

	assert(address != NULL);

	address->sin_port = htons(PMAPPORT);
	client = clntudp_bufcreate(address, PMAPPROG,
	    PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
	if (client != NULL) {
		parms.pm_prog = program;
		parms.pm_vers = version;
		parms.pm_prot = protocol;
		parms.pm_port = 0;  /* not needed or used */
		if (CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
		    (xdrproc_t)xdr_pmap,
		    &parms, (xdrproc_t)xdr_u_short, &port, tottimeout) !=
		    RPC_SUCCESS){
			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
			clnt_geterr(client, &rpc_createerr.cf_error);
		} else if (port == 0) {
			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
		}
		CLNT_DESTROY(client);
	}
	address->sin_port = 0;
	return (port);
}
예제 #9
0
/*
 * Set a mapping between program,version and port.
 * Calls the pmap service remotely to do the mapping.
 */
bool_t
pmap_set(u_long program, u_long version, u_int protocol, int iport)
{
	struct sockaddr_in myaddress;
	int sock = -1;
	CLIENT *client;
	struct pmap parms;
	bool_t rslt;
	u_short port = iport;

	if (get_myaddress(&myaddress) != 0)
		return (FALSE);
	myaddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
	    timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
	if (client == NULL)
		return (FALSE);
	parms.pm_prog = program;
	parms.pm_vers = version;
	parms.pm_prot = protocol;
	parms.pm_port = port;
	if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt,
	    tottimeout) != RPC_SUCCESS) {
		int save_errno = errno;

		clnt_perror(client, "Cannot register service");
		errno = save_errno;
		return (FALSE);
	}
	CLNT_DESTROY(client);
	if (sock != -1)
		(void)close(sock);
	return (rslt);
}
예제 #10
0
파일: pmap_clnt.c 프로젝트: OSLL/elfperf
/*
 * Set a mapping between program,version and port.
 * Calls the pmap service remotely to do the mapping.
 */
bool_t
pmap_set (u_long program, u_long version, int protocol, u_short port)
{
  struct sockaddr_in myaddress;
  int socket = -1;
  CLIENT *client;
  struct pmap parms;
  bool_t rslt;

  if (!__get_myaddress (&myaddress))
    return FALSE;
  client = INTUSE(clntudp_bufcreate) (&myaddress, PMAPPROG, PMAPVERS,
				      timeout, &socket, RPCSMALLMSGSIZE,
				      RPCSMALLMSGSIZE);
  if (client == (CLIENT *) NULL)
    return (FALSE);
  parms.pm_prog = program;
  parms.pm_vers = version;
  parms.pm_prot = protocol;
  parms.pm_port = port;
  if (CLNT_CALL (client, PMAPPROC_SET, (xdrproc_t)INTUSE(xdr_pmap),
		 (caddr_t)&parms, (xdrproc_t)INTUSE(xdr_bool), (caddr_t)&rslt,
		 tottimeout) != RPC_SUCCESS)
    {
      clnt_perror (client, _("Cannot register service"));
      rslt = FALSE;
    }
  CLNT_DESTROY (client);
  /* (void)close(socket); CLNT_DESTROY closes it */
  return rslt;
}
예제 #11
0
파일: pmap_clnt.c 프로젝트: OSLL/elfperf
/*
 * Remove the mapping between program,version and port.
 * Calls the pmap service remotely to do the un-mapping.
 */
bool_t
pmap_unset (u_long program, u_long version)
{
  struct sockaddr_in myaddress;
  int socket = -1;
  CLIENT *client;
  struct pmap parms;
  bool_t rslt;

  if (!__get_myaddress (&myaddress))
    return FALSE;
  client = INTUSE(clntudp_bufcreate) (&myaddress, PMAPPROG, PMAPVERS,
				      timeout, &socket, RPCSMALLMSGSIZE,
				      RPCSMALLMSGSIZE);
  if (client == (CLIENT *) NULL)
    return FALSE;
  parms.pm_prog = program;
  parms.pm_vers = version;
  parms.pm_port = parms.pm_prot = 0;
  CLNT_CALL (client, PMAPPROC_UNSET, (xdrproc_t)INTUSE(xdr_pmap),
	     (caddr_t)&parms, (xdrproc_t)INTUSE(xdr_bool), (caddr_t)&rslt,
	     tottimeout);
  CLNT_DESTROY (client);
  /* (void)close(socket); CLNT_DESTROY already closed it */
  return rslt;
}
예제 #12
0
파일: pmap_clnt.c 프로젝트: aosm/Kerberos
/*
 * Set a mapping between program,version and port.
 * Calls the pmap service remotely to do the mapping.
 */
bool_t
pmap_set(
	rpcprog_t program,
	rpcvers_t version,
	rpcprot_t protocol,
	u_int port)
{
	struct sockaddr_in myaddress;
	int socket = -1;
	register CLIENT *client;
	struct pmap parms;
	bool_t rslt;

	get_myaddress(&myaddress);
	client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
	    timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
	if (client == (CLIENT *)NULL)
		return (FALSE);
	parms.pm_prog = program;
	parms.pm_vers = version;
	parms.pm_prot = protocol;
	parms.pm_port = port;
	if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt,
	    tottimeout) != RPC_SUCCESS) {
		clnt_perror(client, "Cannot register service");
		return (FALSE);
	}
	CLNT_DESTROY(client);
	(void)close(socket);
	return (rslt);
}
예제 #13
0
파일: pm_getmaps.c 프로젝트: AubrCool/glibc
/*
 * Get a copy of the current port maps.
 * Calls the pmap service remotely to do get the maps.
 */
struct pmaplist *
pmap_getmaps (struct sockaddr_in *address)
{
  struct pmaplist *head = (struct pmaplist *) NULL;
  struct timeval minutetimeout;
  CLIENT *client;
  bool closeit = false;

  minutetimeout.tv_sec = 60;
  minutetimeout.tv_usec = 0;
  address->sin_port = htons (PMAPPORT);

  /* Don't need a reserved port to get ports from the portmapper.  */
  int socket = __get_socket (address);
  if (socket != -1)
    closeit = true;

  client = clnttcp_create (address, PMAPPROG, PMAPVERS, &socket, 50, 500);
  if (client != (CLIENT *) NULL)
    {
      if (CLNT_CALL (client, PMAPPROC_DUMP, (xdrproc_t)xdr_void, NULL,
		     (xdrproc_t)xdr_pmaplist, (caddr_t)&head,
		     minutetimeout) != RPC_SUCCESS)
	{
	  clnt_perror (client, _("pmap_getmaps.c: rpc problem"));
	}
      CLNT_DESTROY (client);
    }
  /* We only need to close the socket here if we opened  it.  */
  if (closeit)
    close_not_cancel (socket);
  address->sin_port = 0;
  return head;
}
예제 #14
0
void attribute_hidden __rpc_thread_clnt_cleanup (void)
{
	struct callrpc_private_s *rcp = RPC_THREAD_VARIABLE(callrpc_private_s);

	if (rcp) {
		if (rcp->client)
			CLNT_DESTROY (rcp->client);
		free (rcp);
	}
}
예제 #15
0
static void
rpc_call_destroy(void *vp)
{
	struct rpc_call_private *rcp = (struct rpc_call_private *)vp;

	if (rcp) {
		if (rcp->client)
			CLNT_DESTROY(rcp->client);
		free(rcp);
	}
}
예제 #16
0
static void
clnt_reconnect_destroy(CLIENT *cl)
{
	struct rc_data *rc = (struct rc_data *)cl->cl_private;

	if (rc->rc_client)
		CLNT_DESTROY(rc->rc_client);
	crfree(rc->rc_ucred);
	mtx_destroy(&rc->rc_lock);
	mem_free(rc, sizeof(*rc));
	mem_free(cl, sizeof (CLIENT));
}
예제 #17
0
static void
clnt_reconnect_destroy(CLIENT *cl)
{
	struct rc_data *rc = (struct rc_data *)cl->cl_private;
	SVCXPRT *xprt;

	if (rc->rc_client)
		CLNT_DESTROY(rc->rc_client);
	if (rc->rc_backchannel) {
		xprt = (SVCXPRT *)rc->rc_backchannel;
		xprt_unregister(xprt);
		SVC_RELEASE(xprt);
	}
	crfree(rc->rc_ucred);
	mtx_destroy(&rc->rc_lock);
	mem_free(rc, sizeof(*rc));
	mem_free(cl, sizeof (CLIENT));
}
예제 #18
0
/*
 * This has the same definition as clnt_tp_create(), except it
 * takes an additional parameter - a pointer to a timeval structure.
 * A NULL value for the timeout pointer indicates that the default
 * value for the timeout should be used.
 */
CLIENT *
clnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
    const struct netconfig *nconf, const struct timeval *tp)
{
	struct netbuf *svcaddr;			/* servers address */
	CLIENT *cl = NULL;			/* client handle */

	if (nconf == NULL) {
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return (NULL);
	}

	/*
	 * Get the address of the server
	 */
	if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
			(struct netconfig *)nconf, (char *)hostname,
			&cl, (struct timeval *)tp)) == NULL) {
		/* appropriate error number is set by rpcbind libraries */
		return (NULL);
	}
	if (cl == NULL) {
		cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
					prog, vers, 0, 0);
	} else {
		/* Reuse the CLIENT handle and change the appropriate fields */
		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
			if (cl->cl_netid == NULL)
				cl->cl_netid = strdup(nconf->nc_netid);
			if (cl->cl_tp == NULL)
				cl->cl_tp = strdup(nconf->nc_device);
			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
		} else {
			CLNT_DESTROY(cl);
			cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
					prog, vers, 0, 0);
		}
	}
	free(svcaddr->buf);
	free(svcaddr);
	return (cl);
}
예제 #19
0
파일: pmap_rmt.c 프로젝트: PADL/krb5
/*
 * pmapper remote-call-service interface.
 * This routine is used to call the pmapper remote call service
 * which will look up a service program in the port maps, and then
 * remotely call that routine with the given parameters.  This allows
 * programs to do a lookup and call in one step.
*/
enum clnt_stat
pmap_rmtcall(
	struct sockaddr_in *addr,
	rpcprog_t prog,
	rpcvers_t vers,
	rpcproc_t proc,
	xdrproc_t xdrargs,
	caddr_t argsp,
	xdrproc_t xdrres,
	caddr_t resp,
	struct timeval tout,
	rpcport_t *port_ptr)
{
        SOCKET sock = INVALID_SOCKET;
	CLIENT *client;
	struct rmtcallargs a;
	struct rmtcallres r;
	enum clnt_stat stat;

	addr->sin_port = htons(PMAPPORT);
	client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &sock);
	if (client != (CLIENT *)NULL) {
		a.prog = prog;
		a.vers = vers;
		a.proc = proc;
		a.args_ptr = argsp;
		a.xdr_args = xdrargs;
		r.port_ptr = port_ptr;
		r.results_ptr = resp;
		r.xdr_results = xdrres;
		stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a,
		    xdr_rmtcallres, &r, tout);
		CLNT_DESTROY(client);
	} else {
		stat = RPC_FAILED;
	}
        (void)closesocket(sock);
	addr->sin_port = 0;
	return (stat);
}
예제 #20
0
파일: mount.c 프로젝트: andreiw/polaris
/*
 * This routine will open a device as it is known by the V2 OBP. It
 * then goes thru the stuff necessary to initialize the network device,
 * get our network parameters, (using DHCP or rarp/bootparams), and
 * finally actually go and get the root filehandle. Sound like fun?
 * Suuurrrree. Take a look.
 *
 * Returns 0 if things worked. -1 if we crashed and burned.
 */
int
boot_nfs_mountroot(char *str)
{
	int		status;
	enum clnt_stat	rpc_stat;
	char		*root_path = &root_pathbuf[0];	/* to make XDR happy */
	struct timeval	wait;
	int		fd;
	int		bufsize;
	char		*opts, *val;
	int		nfs_version = 0;
	int		istcp = 1;
	int		nfs_port = 0;	/* Cause pmap to get port */
	struct sockaddr_in tmp_addr;	/* throw away */

	if (root_CLIENT != NULL) {
		AUTH_DESTROY(root_CLIENT->cl_auth);
		CLNT_DESTROY(root_CLIENT);
		root_CLIENT = NULL;
	}

	root_to.sin_family = AF_INET;
	root_to.sin_addr.s_addr = htonl(INADDR_ANY);
	root_to.sin_port = htons(0);

	mac_init(str);

	(void) ipv4_setpromiscuous(TRUE);

	if (get_netconfig_strategy() == NCT_BOOTP_DHCP) {
		if (boothowto & RB_VERBOSE)
			printf("Using BOOTP/DHCP...\n");
		if (dhcp() != 0 || setup_root_vars() != 0) {
			(void) ipv4_setpromiscuous(FALSE);
			if (boothowto & RB_VERBOSE)
				printf("BOOTP/DHCP configuration failed!\n");
			return (-1);
		}

		/* now that we have an IP address, turn off promiscuous mode */
		(void) ipv4_setpromiscuous(FALSE);
	} else {
		/* Use RARP/BOOTPARAMS. RARP will try forever... */
		if (boothowto & RB_VERBOSE)
			printf("Using RARP/BOOTPARAMS...\n");
		mac_call_rarp();

		/*
		 * Since there is no way to determine our netmask, and therefore
		 * figure out if the router we got is useful, we assume all
		 * services are local. Use DHCP if this bothers you.
		 */
		dontroute = TRUE;
		/*
		 * We are trying to keep the ARP response
		 * timeout on the lower side with BOOTP/RARP.
		 * We are doing this for BOOTP/RARP where policy
		 * doesn't allow to route the packets outside
		 * the subnet as it has no idea about the
		 * netmask. By doing so, we are reducing
		 * ARP response timeout for any packet destined
		 * for outside booting clients subnet. Client can
		 * not expect such ARP replies and will finally
		 * timeout after a long delay. This would cause
		 * booting client to get stalled for a longer
		 * time. We can not avoid accepting any outside
		 * subnet packets accidentally destined for the
		 * booting client.
		 */
		mac_set_arp_timeout(ARP_INETBOOT_TIMEOUT);

		/* now that we have an IP address, turn off promiscuous mode */
		(void) ipv4_setpromiscuous(FALSE);

		/* get our hostname */
		if (whoami() == FALSE)
			return (-1);

		/* get our bootparams. */
		if (getfile("root", root_hostname, &root_to.sin_addr,
		    root_pathbuf) == FALSE)
			return (-1);

		/* get our rootopts. */
		(void) getfile("rootopts", root_hostname, &tmp_addr.sin_addr,
		    rootopts);
	}

	/* mount root */
	if (boothowto & RB_VERBOSE) {
		printf("root server: %s (%s)\n", root_hostname,
		    inet_ntoa(root_to.sin_addr));
		printf("root directory: %s\n", root_pathbuf);
	}

	/*
	 * Assumes we've configured the stack and thus know our
	 * IP address/hostname, either by using DHCP or rarp/bootparams.
	 */
	gethostname(my_hostname, sizeof (my_hostname));

	wait.tv_sec = RPC_RCVWAIT_MSEC / 1000;
	wait.tv_usec = 0;

	/*
	 * Parse out the interesting root options, if an invalid
	 * or unknown option is provided, silently ignore it and
	 * use the defaults.
	 */
	opts = rootopts;
	while (*opts) {
		int ival;
		switch (getsubopt(&opts, optlist, &val)) {
		case OPT_RSIZE:
			if (val == NULL || !isdigit(*val))
				break;
			nfs_readsize = atoi(val);
			break;
		case OPT_TIMEO:
			if (val == NULL || !isdigit(*val))
				break;
			ival = atoi(val);
			wait.tv_sec = ival / 10;
			wait.tv_usec = (ival % 10) * 100000;
			break;
		case OPT_VERS:
			if (val == NULL || !isdigit(*val))
				break;
			nfs_version = atoi(val);
			break;
		case OPT_PROTO:
			if (val == NULL || isdigit(*val))
				break;
			if ((strncmp(val, "udp", 3) == 0))
				istcp = 0;
			else
				istcp = 1;	/* must be tcp */
			break;
		case OPT_PORT:
			if (val == NULL || !isdigit(*val))
				break;
			nfs_port = atoi(val);

			/*
			 * Currently nfs_dlinet.c doesn't support setting
			 * the root NFS port. Delete this when it does.
			 */
			nfs_port = 0;
			break;
		default:
			/*
			 * Unknown options are silently ignored
			 */
			break;
		}
	}

	/*
	 * If version is set, then try that version first.
	 */
	switch (nfs_version) {
	case NFS_VERSION:
		if (nfsmountroot(root_path, &roothandle) == 0)
			goto domount;
		break;
	case NFS_V3:
		if (nfs3mountroot(root_path, &roothandle) == 0)
			goto domount;
		break;
	case NFS_V4:
		/*
		 * With v4 we skip the mount and go straight to
		 * setting the root filehandle.  Because of this we
		 * do things slightly differently and obtain our
		 * client handle first.
		 */
		if (istcp && nfs4init(root_path, nfs_port) == 0) {
			/*
			 * If v4 init succeeded then we are done.  Just return.
			 */
			return (0);
		}
	}

	/*
	 * If there was no chosen version or the chosen version failed
	 * try all versions in order, this may still fail to boot
	 * at the kernel level if the options are not right, but be
	 * generous at this early stage.
	 */
	if (istcp && nfs4init(root_path, nfs_port) == 0) {
		/*
		 * If v4 init succeeded then we are done.  Just return.
		 */
		return (0);
	}

	if (nfs3mountroot(root_path, &roothandle) == 0)
		goto domount;

	if ((status = nfsmountroot(root_path, &roothandle)) != 0)
		return (status);

domount:
	/*
	 * Only v2 and v3 go on from here.
	 */
	roothandle.offset = (uint_t)0;		/* it's a directory! */
	root_to.sin_port = htons(nfs_port);	/* NFS is next after mount */

	/*
	 * Create the CLIENT handle for NFS operations
	 */
	if (roothandle.version == NFS_VERSION)
		bufsize = NFSBUF_SIZE;
	else
		bufsize = NFS3BUF_SIZE;

	/*
	 * First try TCP then UDP (unless UDP asked for explicitly), if mountd
	 * alows this version but neither transport is available we are stuck.
	 */
	if (istcp) {
		fd = -1;
		root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM,
			roothandle.version, wait, &fd, bufsize, bufsize);
		if (root_CLIENT != NULL) {
			root_CLIENT->cl_auth =
			    authunix_create(my_hostname, 0, 1, 1, &fake_gids);
			/*
			 * Send NULL proc, check if the server really exists
			 */
			rpc_stat = CLNT_CALL(root_CLIENT, 0,
					xdr_void, NULL, xdr_void, NULL, wait);

			if (rpc_stat == RPC_SUCCESS)
				return (0);

			AUTH_DESTROY(root_CLIENT->cl_auth);
			CLNT_DESTROY(root_CLIENT);
			root_CLIENT = NULL;
		}
		/* Fall through to UDP case */
	}

	fd = -1;
	root_CLIENT = clntbudp_bufcreate(&root_to, NFS_PROGRAM,
			roothandle.version, wait, &fd, bufsize, bufsize);
	if (root_CLIENT == NULL)
		return (-1);

	root_CLIENT->cl_auth =
			    authunix_create(my_hostname, 0, 1, 1, &fake_gids);
	/*
	 * Send NULL proc, check if the server really exists
	 */
	rpc_stat = CLNT_CALL(root_CLIENT, 0,
				xdr_void, NULL, xdr_void, NULL, wait);

	if (rpc_stat == RPC_SUCCESS)
		return (0);

	AUTH_DESTROY(root_CLIENT->cl_auth);
	CLNT_DESTROY(root_CLIENT);
	root_CLIENT = NULL;
	return (-1);
}
예제 #21
0
/*
 * This is the simplified interface to the client rpc layer.
 * The client handle is not destroyed here and is reused for
 * the future calls to same prog, vers, host and nettype combination.
 *
 * The total time available is 25 seconds.
 */
enum clnt_stat
rpc_call(const char *host,			/* host name */
	rpcprog_t prognum,			/* program number */
	rpcvers_t versnum,			/* version number */
	rpcproc_t procnum,			/* procedure number */
	xdrproc_t inproc,
	const char *in,
	xdrproc_t outproc,			/* in/out XDR procedures */
	char  *out,				/* recv/send data */
	const char *nettype)			/* nettype */
{
	struct rpc_call_private *rcp = NULL;
	enum clnt_stat clnt_stat;
	struct timeval timeout, tottimeout;
	static thread_key_t rpc_call_key;
	int main_thread = 1;

	if ((main_thread = thr_main())) {
		rcp = rpc_call_private_main;
	} else {
		if (rpc_call_key == 0) {
			mutex_lock(&tsd_lock);
			if (rpc_call_key == 0)
				thr_keycreate(&rpc_call_key, rpc_call_destroy);
			mutex_unlock(&tsd_lock);
		}
		rcp = (struct rpc_call_private *)thr_getspecific(rpc_call_key);
	}
	if (rcp == NULL) {
		rcp = malloc(sizeof (*rcp));
		if (rcp == NULL) {
			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
			rpc_createerr.cf_error.re_errno = errno;
			return (rpc_createerr.cf_stat);
		}
		if (main_thread)
			rpc_call_private_main = rcp;
		else
			thr_setspecific(rpc_call_key, (void *) rcp);
		rcp->valid = 0;
		rcp->client = NULL;
	}
	if ((nettype == NULL) || (nettype[0] == 0))
		nettype = "netpath";
	if (!(rcp->valid && rcp->pid == getpid() &&
		(rcp->prognum == prognum) &&
		(rcp->versnum == versnum) &&
		(!strcmp(rcp->host, host)) &&
		(!strcmp(rcp->nettype, nettype)))) {
		int fd;

		rcp->valid = 0;
		if (rcp->client)
			CLNT_DESTROY(rcp->client);
		/*
		 * Using the first successful transport for that type
		 */
		rcp->client = clnt_create(host, prognum, versnum, nettype);
		rcp->pid = getpid();
		if (rcp->client == NULL) {
			return (rpc_createerr.cf_stat);
		}
		/*
		 * Set time outs for connectionless case.  Do it
		 * unconditionally.  Faster than doing a t_getinfo()
		 * and then doing the right thing.
		 */
		timeout.tv_usec = 0;
		timeout.tv_sec = 5;
		CLNT_CONTROL(rcp->client,
				CLSET_RETRY_TIMEOUT, (char *)(void *)&timeout);
		if (CLNT_CONTROL(rcp->client, CLGET_FD, (char *)(void *)&fd))
			_fcntl(fd, F_SETFD, 1);	/* make it "close on exec" */
		rcp->prognum = prognum;
		rcp->versnum = versnum;
		if ((strlen(host) < (size_t)MAXHOSTNAMELEN) &&
		    (strlen(nettype) < (size_t)NETIDLEN)) {
			strcpy(rcp->host, host);
			strcpy(rcp->nettype, nettype);
			rcp->valid = 1;
		} else {
			rcp->valid = 0;
		}
	} /* else reuse old client */
	tottimeout.tv_sec = 25;
	tottimeout.tv_usec = 0;
	/*LINTED const castaway*/
	clnt_stat = CLNT_CALL(rcp->client, procnum, inproc, (char *) in,
	    outproc, out, tottimeout);
	/*
	 * if call failed, empty cache
	 */
	if (clnt_stat != RPC_SUCCESS)
		rcp->valid = 0;
	return (clnt_stat);
}
예제 #22
0
파일: rpctest.c 프로젝트: coyizumi/cs111
static void
test_client(int argc, const char **argv)
{
	rpcproc_t prog = 123456;
	rpcvers_t vers = 1;
	const char *netid = "tcp";
	char hostname[128], service[128+5];
	CLIENT *client;
	AUTH *auth;
	const char **mechs;
	rpc_gss_options_req_t options_req;
	rpc_gss_options_ret_t options_ret;
	rpc_gss_service_t svc;
	struct timeval tv;
	enum clnt_stat stat;

	if (argc == 2)
		strlcpy(hostname, argv[1], sizeof(hostname));
	else
		gethostname(hostname, sizeof(hostname));

	client = clnt_create(hostname, prog, vers, netid);
	if (!client) {
		printf("rpc_createerr.cf_stat = %d\n",
		    rpc_createerr.cf_stat);
		printf("rpc_createerr.cf_error.re_errno = %d\n",
		    rpc_createerr.cf_error.re_errno);
		return;
	}
	
	strcpy(service, "host");
	strcat(service, "@");
	strcat(service, hostname);

	mechs = rpc_gss_get_mechanisms();
	auth = NULL;
	while (*mechs) {
		options_req.req_flags = GSS_C_MUTUAL_FLAG;
		options_req.time_req = 600;
		options_req.my_cred = GSS_C_NO_CREDENTIAL;
		options_req.input_channel_bindings = NULL;
		auth = rpc_gss_seccreate(client, service,
		    *mechs,
		    rpc_gss_svc_none,
		    NULL,
		    &options_req,
		    &options_ret);
		if (auth)
			break;
		mechs++;
	}
	if (!auth) {
		clnt_pcreateerror("rpc_gss_seccreate");
		printf("Can't authenticate with server %s.\n",
		    hostname);
		exit(1);
	}
	client->cl_auth = auth;

	for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) {
		const char *svc_names[] = {
			"rpc_gss_svc_default",
			"rpc_gss_svc_none",
			"rpc_gss_svc_integrity",
			"rpc_gss_svc_privacy"
		};
		int num;

		rpc_gss_set_defaults(auth, svc, NULL);
		tv.tv_sec = 5;
		tv.tv_usec = 0;
		num = 42;
		stat = CLNT_CALL(client, 1,
		    (xdrproc_t) xdr_int, (char *) &num,
		    (xdrproc_t) xdr_int, (char *) &num, tv);
		if (stat == RPC_SUCCESS) {
			printf("succeeded with %s\n", svc_names[svc]);
			if (num != 142)
				printf("unexpected reply %d\n", num);
		} else {
			clnt_perror(client, "call failed");
		}
	}
	AUTH_DESTROY(auth);
	CLNT_DESTROY(client);
}
예제 #23
0
파일: network.c 프로젝트: AllardJ/Tomato
static int nfs_probe_statd(void)
{
	struct sockaddr_in addr = {
		.sin_family		= AF_INET,
		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
	};
	rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);

	return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr),
				program, (rpcvers_t)1, IPPROTO_UDP);
}

/**
 * start_statd - attempt to start rpc.statd
 *
 * Returns 1 if statd is running; otherwise zero.
 */
int start_statd(void)
{
#ifdef START_STATD
	struct stat stb;
#endif

	if (nfs_probe_statd())
		return 1;

#ifdef START_STATD
	if (stat(START_STATD, &stb) == 0) {
		if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
			pid_t pid = fork();
			switch (pid) {
			case 0: /* child */
				execl(START_STATD, START_STATD, NULL);
				exit(1);
			case -1: /* error */
				nfs_error(_("fork failed: %s"),
							strerror(errno));
				break;
			default: /* parent */
				waitpid(pid, NULL,0);
				break;
			}
			if (nfs_probe_statd())
				return 1;
		}
	}
#endif

	return 0;
}

/**
 * nfs_advise_umount - ask the server to remove a share from it's rmtab
 * @sap: pointer to IP address of server to call
 * @salen: length of server address
 * @pmap: partially filled-in mountd RPC service tuple
 * @argp: directory path of share to "unmount"
 *
 * Returns one if the unmount call succeeded; zero if the unmount
 * failed for any reason;  rpccreateerr.cf_stat is set to reflect
 * the nature of the error.
 *
 * We use a fast timeout since this call is advisory only.
 */
int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
		      const struct pmap *pmap, const dirpath *argp)
{
	struct sockaddr_storage address;
	struct sockaddr *saddr = (struct sockaddr *)&address;
	struct pmap mnt_pmap = *pmap;
	struct timeval timeout = {
		.tv_sec		= MOUNT_TIMEOUT >> 3,
	};
	CLIENT *client;
	enum clnt_stat res = 0;

	if (nfs_probe_mntport(sap, salen, &mnt_pmap) == 0)
		return 0;

	memcpy(saddr, sap, salen);
	nfs_set_port(saddr, mnt_pmap.pm_port);

	client = nfs_get_rpcclient(saddr, salen, mnt_pmap.pm_prot,
					mnt_pmap.pm_prog, mnt_pmap.pm_vers,
					&timeout);
	if (client == NULL)
		return 0;

	client->cl_auth = authunix_create_default();

	res = CLNT_CALL(client, MOUNTPROC_UMNT,
			(xdrproc_t)xdr_dirpath, (caddr_t)argp,
			(xdrproc_t)xdr_void, NULL,
			timeout);

	auth_destroy(client->cl_auth);
	CLNT_DESTROY(client);

	if (res != RPC_SUCCESS)
		return 0;

	return 1;
}

/**
 * nfs_call_umount - ask the server to remove a share from it's rmtab
 * @mnt_server: address of RPC MNT program server
 * @argp: directory path of share to "unmount"
 *
 * Returns one if the unmount call succeeded; zero if the unmount
 * failed for any reason.
 *
 * Note that a side effect of calling this function is that rpccreateerr
 * is set.
 */
int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
{
	struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr;
	socklen_t salen = sizeof(mnt_server->saddr);
	struct pmap *pmap = &mnt_server->pmap;
	CLIENT *clnt;
	enum clnt_stat res = 0;
	int msock;

	if (!nfs_probe_mntport(sap, salen, pmap))
		return 0;
	clnt = mnt_openclnt(mnt_server, &msock);
	if (!clnt)
		return 0;
	res = clnt_call(clnt, MOUNTPROC_UMNT,
			(xdrproc_t)xdr_dirpath, (caddr_t)argp,
			(xdrproc_t)xdr_void, NULL,
			TIMEOUT);
	mnt_closeclnt(clnt, msock);

	if (res == RPC_SUCCESS)
		return 1;
	return 0;
}

/**
 * mnt_openclnt - get a handle for a remote mountd service
 * @mnt_server: address and pmap arguments of mountd service
 * @msock: returns a file descriptor of the underlying transport socket
 *
 * Returns an active handle for the remote's mountd service
 */
CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
{
	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
	struct pmap *mnt_pmap = &mnt_server->pmap;
	CLIENT *clnt = NULL;

	mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
	*msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
				TRUE, FALSE);
	if (*msock == RPC_ANYSOCK) {
		if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
			/*
			 * Probably in-use by a TIME_WAIT connection,
			 * It is worth waiting a while and trying again.
			 */
			rpc_createerr.cf_stat = RPC_TIMEDOUT;
		return NULL;
	}

	switch (mnt_pmap->pm_prot) {
	case IPPROTO_UDP:
		clnt = clntudp_bufcreate(mnt_saddr,
					 mnt_pmap->pm_prog, mnt_pmap->pm_vers,
					 RETRY_TIMEOUT, msock,
					 MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
		break;
	case IPPROTO_TCP:
		clnt = clnttcp_create(mnt_saddr,
				      mnt_pmap->pm_prog, mnt_pmap->pm_vers,
				      msock,
				      MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
		break;
	}
	if (clnt) {
		/* try to mount hostname:dirname */
		clnt->cl_auth = authunix_create_default();
		return clnt;
	}
	return NULL;
}

/**
 * mnt_closeclnt - terminate a handle for a remote mountd service
 * @clnt: pointer to an active handle for a remote mountd service
 * @msock: file descriptor of the underlying transport socket
 *
 */
void mnt_closeclnt(CLIENT *clnt, int msock)
{
	auth_destroy(clnt->cl_auth);
	clnt_destroy(clnt);
	close(msock);
}

/**
 * clnt_ping - send an RPC ping to the remote RPC service endpoint
 * @saddr: server's address
 * @prog: target RPC program number
 * @vers: target RPC version number
 * @prot: target RPC protocol
 * @caddr: filled in with our network address
 *
 * Sigh... GETPORT queries don't actually check the version number.
 * In order to make sure that the server actually supports the service
 * we're requesting, we open an RPC client, and fire off a NULL
 * RPC call.
 *
 * caddr is the network address that the server will use to call us back.
 * On multi-homed clients, this address depends on which NIC we use to
 * route requests to the server.
 *
 * Returns one if successful, otherwise zero.
 */
int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
		const unsigned long vers, const unsigned int prot,
		struct sockaddr_in *caddr)
{
	CLIENT *clnt = NULL;
	int sock, stat;
	static char clnt_res;
	struct sockaddr dissolve;

	rpc_createerr.cf_stat = stat = 0;
	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
	if (sock == RPC_ANYSOCK) {
		if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
			/*
			 * TCP timeout. Bubble up the error to see 
			 * how it should be handled.
			 */
			rpc_createerr.cf_stat = RPC_TIMEDOUT;
		}
		return 0;
	}

	if (caddr) {
		/* Get the address of our end of this connection */
		socklen_t len = sizeof(*caddr);
		if (getsockname(sock, caddr, &len) != 0)
			caddr->sin_family = 0;
	}

	switch(prot) {
	case IPPROTO_UDP:
		/* The socket is connected (so we could getsockname successfully),
		 * but some servers on multi-homed hosts reply from
		 * the wrong address, so if we stay connected, we lose the reply.
		 */
		dissolve.sa_family = AF_UNSPEC;
		connect(sock, &dissolve, sizeof(dissolve));

		clnt = clntudp_bufcreate(saddr, prog, vers,
					 RETRY_TIMEOUT, &sock,
					 RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
		break;
	case IPPROTO_TCP:
		clnt = clnttcp_create(saddr, prog, vers, &sock,
				      RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
		break;
	}
	if (!clnt) {
		close(sock);
		return 0;
	}
	memset(&clnt_res, 0, sizeof(clnt_res));
	stat = clnt_call(clnt, NULLPROC,
			 (xdrproc_t)xdr_void, (caddr_t)NULL,
			 (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
			 TIMEOUT);
	if (stat) {
		clnt_geterr(clnt, &rpc_createerr.cf_error);
		rpc_createerr.cf_stat = stat;
	}
	clnt_destroy(clnt);
	close(sock);

	if (stat == RPC_SUCCESS)
		return 1;
	else
		return 0;
}
예제 #24
0
파일: mount.c 프로젝트: andreiw/polaris
/*
 * Setup v4 client for inetboot
 */
static int
nfs4init(char *path, uint16_t nfs_port)
{
	struct timeval	wait;
	int		fd = -1;
	int		error = 0;
	enum clnt_stat	rpc_stat;
	struct nfs_file	rootpath;

	wait.tv_sec = RPC_RCVWAIT_MSEC / 1000;
	wait.tv_usec = 0;

	/*
	 * If we haven't explicitly set the port number, set to the standard
	 * 2049 and don't cause a rpcbind request.
	 */
	if (nfs_port == 0)
		nfs_port = 2049;

	root_to.sin_port = htons(nfs_port);

	/*
	 * Support TCP only
	 */
	root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM,
					NFS_V4, wait, &fd,
					NFS4BUF_SIZE, NFS4BUF_SIZE);

	if (root_CLIENT == NULL) {
		root_to.sin_port = 0;
		return (-1);
	}

	root_CLIENT->cl_auth =
			authunix_create(my_hostname, 0, 1, 1, &fake_gids);

	/*
	 * Send NULL proc the server first to see if V4 exists
	 */
	rpc_stat = CLNT_CALL(root_CLIENT, NFSPROC4_NULL, xdr_void, NULL,
				xdr_void, NULL, wait);

	if (rpc_stat != RPC_SUCCESS) {
		dprintf("boot: NULL proc failed NFSv4 service not available\n");
		AUTH_DESTROY(root_CLIENT->cl_auth);
		CLNT_DESTROY(root_CLIENT);
		root_to.sin_port = 0;
		return (-1);
	}

	/*
	 * Do a lookup to get to the root_path.  This is nice since it can
	 * handle multicomponent lookups.
	 */
	roothandle.version = NFS_V4;
	roothandle.ftype.type4 = NF4DIR;
	roothandle.fh.fh4.len = 0;		/* Force a PUTROOTFH */
	roothandle.offset = (uint_t)0;		/* it's a directory! */
	error = lookup(path, &rootpath, TRUE);

	if (error) {
		printf("boot: lookup %s failed\n", path);
		return (-1);
	}
	roothandle = rootpath;	/* structure copy */

	/*
	 * Hardwire in a known reasonable upper limit of 32K
	 */
	nfs_readsize = nfs_readsize <  32 * 1024 ? nfs_readsize : 32 * 1024;
	/*
	 * Set a reasonable lower limit on readsize
	 */
	nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ?
							512 : nfs_readsize;

	return (0);
}
예제 #25
0
/*
 * This is the simplified interface to the client rpc layer.
 * The client handle is not destroyed here and is reused for
 * the future calls to same prog, vers, host and nettype combination.
 *
 * The total time available is 25 seconds.
 */
enum clnt_stat
rpc_call(const char *host, const rpcprog_t prognum, const rpcvers_t versnum,
	const rpcproc_t procnum, const xdrproc_t inproc, const char *in,
	const xdrproc_t outproc, char  *out, const char *netclass)
{
	struct rpc_call_private *rcp;
	enum clnt_stat clnt_stat;
	struct timeval timeout, tottimeout;
	static pthread_key_t rpc_call_key = PTHREAD_ONCE_KEY_NP;
	char nettype_array[NETIDLEN];
	char *nettype = &nettype_array[0];

	if (netclass == NULL)
		nettype = NULL;
	else {
		size_t len = strlen(netclass);
		if (len >= sizeof (nettype_array)) {
			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
			return (rpc_createerr.cf_stat);
		}
		(void) strcpy(nettype, netclass);
	}

	rcp = thr_get_storage(&rpc_call_key, sizeof (*rcp), rpc_call_destroy);
	if (rcp == NULL) {
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
		rpc_createerr.cf_error.re_errno = errno;
		return (rpc_createerr.cf_stat);
	}

	if ((nettype == NULL) || (nettype[0] == NULL))
		nettype = "netpath";
	if (!(rcp->valid &&
	    rcp->pid == getpid() &&
	    rcp->prognum == prognum &&
	    rcp->versnum == versnum &&
	    strcmp(rcp->host, host) == 0 &&
	    strcmp(rcp->nettype, nettype) == 0)) {
		int fd;

		rcp->valid = 0;
		if (rcp->client)
			CLNT_DESTROY(rcp->client);
		/*
		 * Using the first successful transport for that type
		 */
		rcp->client = clnt_create(host, prognum, versnum, nettype);
		rcp->pid = getpid();
		if (rcp->client == NULL)
			return (rpc_createerr.cf_stat);
		/*
		 * Set time outs for connectionless case.  Do it
		 * unconditionally.  Faster than doing a t_getinfo()
		 * and then doing the right thing.
		 */
		timeout.tv_usec = 0;
		timeout.tv_sec = 5;
		(void) CLNT_CONTROL(rcp->client,
				CLSET_RETRY_TIMEOUT, (char *)&timeout);
		if (CLNT_CONTROL(rcp->client, CLGET_FD, (char *)&fd))
			(void) fcntl(fd, F_SETFD, 1);	/* close on exec */
		rcp->prognum = prognum;
		rcp->versnum = versnum;
		if ((strlen(host) < (size_t)MAXHOSTNAMELEN) &&
		    (strlen(nettype) < (size_t)NETIDLEN)) {
			(void) strcpy(rcp->host, host);
			(void) strcpy(rcp->nettype, nettype);
			rcp->valid = 1;
		} else {
			rcp->valid = 0;
		}
	} /* else reuse old client */
	tottimeout.tv_sec = 25;
	tottimeout.tv_usec = 0;
	clnt_stat = CLNT_CALL(rcp->client, procnum, inproc, (char *)in,
				outproc, out, tottimeout);
	/*
	 * if call failed, empty cache
	 */
	if (clnt_stat != RPC_SUCCESS)
		rcp->valid = 0;
	return (clnt_stat);
}