174462Salfred/* $NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $ */ 274462Salfred 31901Swollman/* 499775Salfred * The contents of this file are subject to the Sun Standards 599775Salfred * License Version 1.0 the (the "License";) You may not use 699775Salfred * this file except in compliance with the License. You may 799775Salfred * obtain a copy of the License at lib/libc/rpc/LICENSE 899775Salfred * 999775Salfred * Software distributed under the License is distributed on 1099775Salfred * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 1199775Salfred * express or implied. See the License for the specific 1299775Salfred * language governing rights and limitations under the License. 1399775Salfred * 1499775Salfred * The Original Code is Copyright 1998 by Sun Microsystems, Inc 1599775Salfred * 1699775Salfred * The Initial Developer of the Original Code is: Sun 1799775Salfred * Microsystems, Inc. 1899775Salfred * 1999775Salfred * All Rights Reserved. 2099775Salfred * 211901Swollman * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 221901Swollman * unrestricted use provided that this legend is included on all tape 231901Swollman * media and as a part of the software program in whole or part. Users 241901Swollman * may copy or modify Sun RPC without charge, but are not authorized 251901Swollman * to license or distribute it to anyone else except as part of a product or 261901Swollman * program developed by the user. 278870Srgrimes * 281901Swollman * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 291901Swollman * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 301901Swollman * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 318870Srgrimes * 321901Swollman * Sun RPC is provided with no support and without any obligation on the 331901Swollman * part of Sun Microsystems, Inc. to assist in its use, correction, 341901Swollman * modification or enhancement. 351901Swollman * 361901Swollman * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 371901Swollman * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 381901Swollman * OR ANY PART THEREOF. 398870Srgrimes * 401901Swollman * In no event will Sun Microsystems, Inc. be liable for any lost revenue 411901Swollman * or profits or other special, indirect and consequential damages, even if 421901Swollman * Sun has been advised of the possibility of such damages. 438870Srgrimes * 441901Swollman * Sun Microsystems, Inc. 451901Swollman * 2550 Garcia Avenue 461901Swollman * Mountain View, California 94043 471901Swollman */ 481901Swollman 4999775Salfred/* #ident "@(#)clnt_generic.c 1.40 99/04/21 SMI" */ 5074462Salfred 511901Swollman#if defined(LIBC_SCCS) && !defined(lint) 52136581Sobrienstatic char *sccsid2 = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI"; 53136581Sobrienstatic char *sccsid = "from: @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC"; 541901Swollman#endif 5592990Sobrien#include <sys/cdefs.h> 5692990Sobrien__FBSDID("$FreeBSD$"); 571901Swollman 581901Swollman/* 5999775Salfred * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc. 6099775Salfred * All rights reserved. 611901Swollman */ 6275094Siedowse#include "namespace.h" 6374462Salfred#include "reentrant.h" 6474462Salfred#include <sys/types.h> 6599775Salfred#include <sys/fcntl.h> 661901Swollman#include <sys/socket.h> 6774462Salfred#include <netinet/in.h> 6874462Salfred#include <netinet/tcp.h> 6974462Salfred#include <stdio.h> 7071579Sdeischen#include <errno.h> 711901Swollman#include <netdb.h> 7299775Salfred#include <syslog.h> 7374462Salfred#include <rpc/rpc.h> 7474462Salfred#include <rpc/nettype.h> 7511666Sphk#include <string.h> 7674462Salfred#include <stdlib.h> 7774462Salfred#include <unistd.h> 7874462Salfred#include "un-namespace.h" 7974462Salfred#include "rpc_com.h" 801901Swollman 8199775Salfredextern bool_t __rpc_is_local_host(const char *); 8299775Salfredint __rpc_raise_fd(int); 8399775Salfred 8499775Salfred#ifndef NETIDLEN 8599775Salfred#define NETIDLEN 32 8699775Salfred#endif 8799775Salfred 8899775Salfred 891901Swollman/* 9074462Salfred * Generic client creation with version checking the value of 9174462Salfred * vers_out is set to the highest server supported value 9274462Salfred * vers_low <= vers_out <= vers_high AND an error results 9374462Salfred * if this can not be done. 9499775Salfred * 9599775Salfred * It calls clnt_create_vers_timed() with a NULL value for the timeout 9699775Salfred * pointer, which indicates that the default timeout should be used. 9774462Salfred */ 9874462SalfredCLIENT * 9999775Salfredclnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out, 10099775Salfred rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype) 10174462Salfred{ 10299775Salfred 10399775Salfred return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low, 10499775Salfred vers_high, nettype, NULL)); 10599775Salfred} 10699775Salfred 10799775Salfred/* 10899775Salfred * This the routine has the same definition as clnt_create_vers(), 10999775Salfred * except it takes an additional timeout parameter - a pointer to 11099775Salfred * a timeval structure. A NULL value for the pointer indicates 11199775Salfred * that the default timeout value should be used. 11299775Salfred */ 11399775SalfredCLIENT * 11499775Salfredclnt_create_vers_timed(const char *hostname, rpcprog_t prog, 11599775Salfred rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high, 11699775Salfred const char *nettype, const struct timeval *tp) 11799775Salfred{ 11874462Salfred CLIENT *clnt; 11974462Salfred struct timeval to; 12074462Salfred enum clnt_stat rpc_stat; 12174462Salfred struct rpc_err rpcerr; 12274462Salfred 12399775Salfred clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp); 12474462Salfred if (clnt == NULL) { 12574462Salfred return (NULL); 12674462Salfred } 12774462Salfred to.tv_sec = 10; 12874462Salfred to.tv_usec = 0; 12999775Salfred rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, 13099775Salfred (char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to); 13174462Salfred if (rpc_stat == RPC_SUCCESS) { 13274462Salfred *vers_out = vers_high; 13374462Salfred return (clnt); 13474462Salfred } 13599775Salfred while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) { 13699775Salfred unsigned int minvers, maxvers; 13774462Salfred 13874462Salfred clnt_geterr(clnt, &rpcerr); 13974462Salfred minvers = rpcerr.re_vers.low; 14074462Salfred maxvers = rpcerr.re_vers.high; 14174462Salfred if (maxvers < vers_high) 14299775Salfred vers_high = maxvers; 14399775Salfred else 14499775Salfred vers_high--; 14574462Salfred if (minvers > vers_low) 14699775Salfred vers_low = minvers; 14774462Salfred if (vers_low > vers_high) { 14874462Salfred goto error; 14974462Salfred } 15099775Salfred CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high); 15199775Salfred rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, 15299775Salfred (char *)NULL, (xdrproc_t)xdr_void, 15399775Salfred (char *)NULL, to); 15474462Salfred if (rpc_stat == RPC_SUCCESS) { 15574462Salfred *vers_out = vers_high; 15674462Salfred return (clnt); 15774462Salfred } 15874462Salfred } 15974462Salfred clnt_geterr(clnt, &rpcerr); 16074462Salfred 16174462Salfrederror: 16274462Salfred rpc_createerr.cf_stat = rpc_stat; 16374462Salfred rpc_createerr.cf_error = rpcerr; 16474462Salfred clnt_destroy(clnt); 16574462Salfred return (NULL); 16674462Salfred} 16774462Salfred 16874462Salfred/* 16974462Salfred * Top level client creation routine. 17074462Salfred * Generic client creation: takes (servers name, program-number, nettype) and 1718870Srgrimes * returns client handle. Default options are set, which the user can 17271579Sdeischen * change using the rpc equivalent of _ioctl()'s. 17374462Salfred * 17474462Salfred * It tries for all the netids in that particular class of netid until 17574462Salfred * it succeeds. 17674462Salfred * XXX The error message in the case of failure will be the one 17774462Salfred * pertaining to the last create error. 17874462Salfred * 17999775Salfred * It calls clnt_create_timed() with the default timeout. 1801901Swollman */ 1811901SwollmanCLIENT * 18299775Salfredclnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers, 18399775Salfred const char *nettype) 1841901Swollman{ 18599775Salfred 18699775Salfred return (clnt_create_timed(hostname, prog, vers, nettype, NULL)); 18799775Salfred} 18899775Salfred 18999775Salfred/* 19099775Salfred * This the routine has the same definition as clnt_create(), 19199775Salfred * except it takes an additional timeout parameter - a pointer to 19299775Salfred * a timeval structure. A NULL value for the pointer indicates 19399775Salfred * that the default timeout value should be used. 19499775Salfred * 19599775Salfred * This function calls clnt_tp_create_timed(). 19699775Salfred */ 19799775SalfredCLIENT * 19899775Salfredclnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers, 19999775Salfred const char *netclass, const struct timeval *tp) 20099775Salfred{ 20174462Salfred struct netconfig *nconf; 20274462Salfred CLIENT *clnt = NULL; 20374462Salfred void *handle; 20474462Salfred enum clnt_stat save_cf_stat = RPC_SUCCESS; 20574462Salfred struct rpc_err save_cf_error; 20699775Salfred char nettype_array[NETIDLEN]; 20799775Salfred char *nettype = &nettype_array[0]; 2081901Swollman 20999775Salfred if (netclass == NULL) 21099775Salfred nettype = NULL; 21199775Salfred else { 21299775Salfred size_t len = strlen(netclass); 21399775Salfred if (len >= sizeof (nettype_array)) { 21499775Salfred rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 21599775Salfred return (NULL); 21699775Salfred } 21799775Salfred strcpy(nettype, netclass); 21899775Salfred } 21926221Swpaul 22099775Salfred if ((handle = __rpc_setconf((char *)nettype)) == NULL) { 22174462Salfred rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 2221901Swollman return (NULL); 2231901Swollman } 22474462Salfred rpc_createerr.cf_stat = RPC_SUCCESS; 22574462Salfred while (clnt == NULL) { 22674462Salfred if ((nconf = __rpc_getconf(handle)) == NULL) { 22774462Salfred if (rpc_createerr.cf_stat == RPC_SUCCESS) 22874462Salfred rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 22974462Salfred break; 23074462Salfred } 23174462Salfred#ifdef CLNT_DEBUG 23274462Salfred printf("trying netid %s\n", nconf->nc_netid); 23374462Salfred#endif 23499775Salfred clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp); 23574462Salfred if (clnt) 23674462Salfred break; 23774462Salfred else 23874462Salfred /* 23974462Salfred * Since we didn't get a name-to-address 24074462Salfred * translation failure here, we remember 24174462Salfred * this particular error. The object of 24274462Salfred * this is to enable us to return to the 24374462Salfred * caller a more-specific error than the 24474462Salfred * unhelpful ``Name to address translation 24574462Salfred * failed'' which might well occur if we 24674462Salfred * merely returned the last error (because 24774462Salfred * the local loopbacks are typically the 24874462Salfred * last ones in /etc/netconfig and the most 24974462Salfred * likely to be unable to translate a host 25099775Salfred * name). We also check for a more 25199775Salfred * meaningful error than ``unknown host 25299775Salfred * name'' for the same reasons. 25374462Salfred */ 25499775Salfred if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE && 25599775Salfred rpc_createerr.cf_stat != RPC_UNKNOWNHOST) { 25674462Salfred save_cf_stat = rpc_createerr.cf_stat; 25774462Salfred save_cf_error = rpc_createerr.cf_error; 25874462Salfred } 2591901Swollman } 26074462Salfred 26174462Salfred /* 26274462Salfred * Attempt to return an error more specific than ``Name to address 26399775Salfred * translation failed'' or ``unknown host name'' 26474462Salfred */ 26599775Salfred if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE || 26699775Salfred rpc_createerr.cf_stat == RPC_UNKNOWNHOST) && 26799775Salfred (save_cf_stat != RPC_SUCCESS)) { 26874462Salfred rpc_createerr.cf_stat = save_cf_stat; 26974462Salfred rpc_createerr.cf_error = save_cf_error; 27074462Salfred } 27174462Salfred __rpc_endconf(handle); 27274462Salfred return (clnt); 27374462Salfred} 27474462Salfred 27574462Salfred/* 27674462Salfred * Generic client creation: takes (servers name, program-number, netconf) and 27774462Salfred * returns client handle. Default options are set, which the user can 27874462Salfred * change using the rpc equivalent of _ioctl()'s : clnt_control() 27999775Salfred * It finds out the server address from rpcbind and calls clnt_tli_create(). 28099775Salfred * 28199775Salfred * It calls clnt_tp_create_timed() with the default timeout. 28274462Salfred */ 28374462SalfredCLIENT * 28499775Salfredclnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers, 28599775Salfred const struct netconfig *nconf) 28674462Salfred{ 28799775Salfred 28899775Salfred return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL)); 28999775Salfred} 29099775Salfred 29199775Salfred/* 29299775Salfred * This has the same definition as clnt_tp_create(), except it 29399775Salfred * takes an additional parameter - a pointer to a timeval structure. 29499775Salfred * A NULL value for the timeout pointer indicates that the default 29599775Salfred * value for the timeout should be used. 29699775Salfred */ 29799775SalfredCLIENT * 29899775Salfredclnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers, 29999775Salfred const struct netconfig *nconf, const struct timeval *tp) 30099775Salfred{ 30174462Salfred struct netbuf *svcaddr; /* servers address */ 30274462Salfred CLIENT *cl = NULL; /* client handle */ 30374462Salfred 30474462Salfred if (nconf == NULL) { 3051901Swollman rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 3061901Swollman return (NULL); 3071901Swollman } 30874462Salfred 30974462Salfred /* 31074462Salfred * Get the address of the server 31174462Salfred */ 31299775Salfred if ((svcaddr = __rpcb_findaddr_timed(prog, vers, 31399775Salfred (struct netconfig *)nconf, (char *)hostname, 31499775Salfred &cl, (struct timeval *)tp)) == NULL) { 31574462Salfred /* appropriate error number is set by rpcbind libraries */ 31674462Salfred return (NULL); 31774462Salfred } 31874462Salfred if (cl == NULL) { 31974462Salfred cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, 32074462Salfred prog, vers, 0, 0); 32174462Salfred } else { 32274462Salfred /* Reuse the CLIENT handle and change the appropriate fields */ 32374462Salfred if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) { 32474462Salfred if (cl->cl_netid == NULL) 32574462Salfred cl->cl_netid = strdup(nconf->nc_netid); 32674462Salfred if (cl->cl_tp == NULL) 32774462Salfred cl->cl_tp = strdup(nconf->nc_device); 32874462Salfred (void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog); 32974462Salfred (void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers); 33074462Salfred } else { 33174462Salfred CLNT_DESTROY(cl); 33274462Salfred cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, 33374462Salfred prog, vers, 0, 0); 3341901Swollman } 33574462Salfred } 33674462Salfred free(svcaddr->buf); 33774462Salfred free(svcaddr); 33874462Salfred return (cl); 33974462Salfred} 34074462Salfred 34174462Salfred/* 34274462Salfred * Generic client creation: returns client handle. 34374462Salfred * Default options are set, which the user can 34474462Salfred * change using the rpc equivalent of _ioctl()'s : clnt_control(). 34574462Salfred * If fd is RPC_ANYFD, it will be opened using nconf. 34674462Salfred * It will be bound if not so. 34774462Salfred * If sizes are 0; appropriate defaults will be chosen. 34874462Salfred */ 34974462SalfredCLIENT * 35099775Salfredclnt_tli_create(int fd, const struct netconfig *nconf, 35199775Salfred struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers, 35299775Salfred uint sendsz, uint recvsz) 35374462Salfred{ 35474462Salfred CLIENT *cl; /* client handle */ 35574462Salfred bool_t madefd = FALSE; /* whether fd opened here */ 35674462Salfred long servtype; 35774462Salfred int one = 1; 35874462Salfred struct __rpc_sockinfo si; 35999775Salfred extern int __rpc_minfd; 36074462Salfred 36174462Salfred if (fd == RPC_ANYFD) { 36274462Salfred if (nconf == NULL) { 36374462Salfred rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 3641901Swollman return (NULL); 3651901Swollman } 36674462Salfred 36774462Salfred fd = __rpc_nconf2fd(nconf); 36874462Salfred 36974462Salfred if (fd == -1) 37074462Salfred goto err; 37199775Salfred if (fd < __rpc_minfd) 37299775Salfred fd = __rpc_raise_fd(fd); 37374462Salfred madefd = TRUE; 37474462Salfred servtype = nconf->nc_semantics; 37574462Salfred if (!__rpc_fd2sockinfo(fd, &si)) 37674462Salfred goto err; 37774462Salfred bindresvport(fd, NULL); 37874462Salfred } else { 37974462Salfred if (!__rpc_fd2sockinfo(fd, &si)) 38074462Salfred goto err; 38174462Salfred servtype = __rpc_socktype2seman(si.si_socktype); 38274462Salfred if (servtype == -1) { 38374462Salfred rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 38499775Salfred return (NULL); 38574462Salfred } 38674462Salfred } 38774462Salfred 38874462Salfred if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) { 38974462Salfred rpc_createerr.cf_stat = RPC_UNKNOWNHOST; /* XXX */ 39074462Salfred goto err1; 39174462Salfred } 39274462Salfred 39374462Salfred switch (servtype) { 39499775Salfred case NC_TPI_COTS: 39599775Salfred cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz); 39699775Salfred break; 39774462Salfred case NC_TPI_COTS_ORD: 39899775Salfred if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) { 39974462Salfred _setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, 40074462Salfred sizeof (one)); 40199775Salfred } 40299775Salfred cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz); 4031901Swollman break; 40474462Salfred case NC_TPI_CLTS: 40574462Salfred cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz); 40674462Salfred break; 4071901Swollman default: 40874462Salfred goto err; 4091901Swollman } 41074462Salfred 41174462Salfred if (cl == NULL) 41274462Salfred goto err1; /* borrow errors from clnt_dg/vc creates */ 41374462Salfred if (nconf) { 41474462Salfred cl->cl_netid = strdup(nconf->nc_netid); 41574462Salfred cl->cl_tp = strdup(nconf->nc_device); 41674462Salfred } else { 41774462Salfred cl->cl_netid = ""; 41874462Salfred cl->cl_tp = ""; 41974462Salfred } 42074462Salfred if (madefd) { 42174462Salfred (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL); 42299775Salfred/* (void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL); */ 42374462Salfred }; 42474462Salfred 42574462Salfred return (cl); 42674462Salfred 42774462Salfrederr: 42874462Salfred rpc_createerr.cf_stat = RPC_SYSTEMERROR; 42974462Salfred rpc_createerr.cf_error.re_errno = errno; 43074462Salfrederr1: if (madefd) 43174462Salfred (void)_close(fd); 43274462Salfred return (NULL); 4331901Swollman} 43499775Salfred 43599775Salfred/* 43699775Salfred * To avoid conflicts with the "magic" file descriptors (0, 1, and 2), 43799775Salfred * we try to not use them. The __rpc_raise_fd() routine will dup 43899775Salfred * a descriptor to a higher value. If we fail to do it, we continue 43999775Salfred * to use the old one (and hope for the best). 44099775Salfred */ 44199775Salfredint __rpc_minfd = 3; 44299775Salfred 44399775Salfredint 44499775Salfred__rpc_raise_fd(int fd) 44599775Salfred{ 44699775Salfred int nfd; 44799775Salfred 44899775Salfred if (fd >= __rpc_minfd) 44999775Salfred return (fd); 45099775Salfred 45199775Salfred if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1) 45299775Salfred return (fd); 45399775Salfred 45499775Salfred if (_fsync(nfd) == -1) { 45599775Salfred _close(nfd); 45699775Salfred return (fd); 45799775Salfred } 45899775Salfred 45999775Salfred if (_close(fd) == -1) { 46099775Salfred /* this is okay, we will syslog an error, then use the new fd */ 46199775Salfred (void) syslog(LOG_ERR, 46299775Salfred "could not close() fd %d; mem & fd leak", fd); 46399775Salfred } 46499775Salfred 46599775Salfred return (nfd); 46699775Salfred} 467