clnt_generic.c revision 1219:f89f56c2d9ac
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29/* All Rights Reserved */
30/*
31 * Portions of this source code were derived from Berkeley
32 * 4.3 BSD under license from the Regents of the University of
33 * California.
34 */
35
36#pragma ident	"%Z%%M%	%I%	%E% SMI"
37
38#include "mt.h"
39#include "rpc_mt.h"
40#include <stdio.h>
41#include <errno.h>
42#include <unistd.h>
43#include <stdlib.h>
44#include <rpc/rpc.h>
45#include <rpc/nettype.h>
46#include <netdir.h>
47#include <string.h>
48#include <syslog.h>
49
50extern int __td_setnodelay(int);
51extern bool_t __rpc_is_local_host(const char *);
52extern bool_t __rpc_try_doors(const char *, bool_t *);
53extern CLIENT *_clnt_vc_create_timed(int, struct netbuf *, rpcprog_t,
54			rpcvers_t, uint_t, uint_t, const struct timeval *);
55
56CLIENT *_clnt_tli_create_timed(int, const struct netconfig *, struct netbuf *,
57		rpcprog_t, rpcvers_t, uint_t, uint_t, const struct timeval *);
58
59#ifndef NETIDLEN
60#define	NETIDLEN 32
61#endif
62
63/*
64 * Generic client creation with version checking the value of
65 * vers_out is set to the highest server supported value
66 * vers_low <= vers_out <= vers_high  AND an error results
67 * if this can not be done.
68 *
69 * It calls clnt_create_vers_timed() with a NULL value for the timeout
70 * pointer, which indicates that the default timeout should be used.
71 */
72CLIENT *
73clnt_create_vers(const char *hostname, const rpcprog_t prog,
74	rpcvers_t *vers_out, const rpcvers_t vers_low,
75	const rpcvers_t vers_high, const char *nettype)
76{
77	return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
78				vers_high, nettype, NULL));
79}
80
81/*
82 * This routine has the same definition as clnt_create_vers(),
83 * except it takes an additional timeout parameter - a pointer to
84 * a timeval structure.  A NULL value for the pointer indicates
85 * that the default timeout value should be used.
86 */
87CLIENT *
88clnt_create_vers_timed(const char *hostname, const rpcprog_t prog,
89    rpcvers_t *vers_out, const rpcvers_t vers_low, const rpcvers_t vers_high,
90    const char *nettype, const struct timeval *tp)
91{
92	CLIENT *clnt;
93	struct timeval to;
94	enum clnt_stat rpc_stat;
95	struct rpc_err rpcerr;
96	rpcvers_t v_low, v_high;
97
98	clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
99	if (clnt == NULL)
100		return (NULL);
101	if (tp == NULL) {
102		to.tv_sec = 10;
103		to.tv_usec = 0;
104	} else
105		to = *tp;
106
107	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
108			NULL, (xdrproc_t)xdr_void, NULL, to);
109	if (rpc_stat == RPC_SUCCESS) {
110		*vers_out = vers_high;
111		return (clnt);
112	}
113	v_low = vers_low;
114	v_high = vers_high;
115	while (rpc_stat == RPC_PROGVERSMISMATCH && v_high > v_low) {
116		unsigned int minvers, maxvers;
117
118		clnt_geterr(clnt, &rpcerr);
119		minvers = rpcerr.re_vers.low;
120		maxvers = rpcerr.re_vers.high;
121		if (maxvers < v_high)
122			v_high = maxvers;
123		else
124			v_high--;
125		if (minvers > v_low)
126			v_low = minvers;
127		if (v_low > v_high) {
128			goto error;
129		}
130		CLNT_CONTROL(clnt, CLSET_VERS, (char *)&v_high);
131		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
132				NULL, (xdrproc_t)xdr_void,
133				NULL, to);
134		if (rpc_stat == RPC_SUCCESS) {
135			*vers_out = v_high;
136			return (clnt);
137		}
138	}
139	clnt_geterr(clnt, &rpcerr);
140
141error:
142	rpc_createerr.cf_stat = rpc_stat;
143	rpc_createerr.cf_error = rpcerr;
144	clnt_destroy(clnt);
145	return (NULL);
146}
147
148/*
149 * Top level client creation routine.
150 * Generic client creation: takes (servers name, program-number, nettype) and
151 * returns client handle. Default options are set, which the user can
152 * change using the rpc equivalent of ioctl()'s.
153 *
154 * It tries for all the netids in that particular class of netid until
155 * it succeeds.
156 * XXX The error message in the case of failure will be the one
157 * pertaining to the last create error.
158 *
159 * It calls clnt_create_timed() with the default timeout.
160 */
161CLIENT *
162clnt_create(const char *hostname, const rpcprog_t prog, const rpcvers_t vers,
163    const char *nettype)
164{
165	return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
166}
167
168/*
169 * This the routine has the same definition as clnt_create(),
170 * except it takes an additional timeout parameter - a pointer to
171 * a timeval structure.  A NULL value for the pointer indicates
172 * that the default timeout value should be used.
173 *
174 * This function calls clnt_tp_create_timed().
175 */
176CLIENT *
177clnt_create_timed(const char *hostname, const rpcprog_t prog,
178    const rpcvers_t vers, const char *netclass, const struct timeval *tp)
179{
180	struct netconfig *nconf;
181	CLIENT *clnt = NULL;
182	void *handle;
183	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
184	struct rpc_err	save_cf_error;
185	char nettype_array[NETIDLEN];
186	char *nettype = &nettype_array[0];
187	bool_t try_others;
188
189	if (netclass == NULL)
190		nettype = NULL;
191	else {
192		size_t len = strlen(netclass);
193		if (len >= sizeof (nettype_array)) {
194			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
195			return (NULL);
196		}
197		(void) strcpy(nettype, netclass);
198	}
199
200	/*
201	 * Check to see if a rendezvous over doors should be attempted.
202	 */
203	if (__rpc_try_doors(nettype, &try_others)) {
204		/*
205		 * Make sure this is the local host.
206		 */
207		if (__rpc_is_local_host(hostname)) {
208			if ((clnt = clnt_door_create(prog, vers, 0)) != NULL)
209				return (clnt);
210			else {
211				if (rpc_createerr.cf_stat == RPC_SYSTEMERROR)
212					return (NULL);
213				save_cf_stat = rpc_createerr.cf_stat;
214				save_cf_error = rpc_createerr.cf_error;
215			}
216		} else {
217			save_cf_stat = rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
218		}
219	}
220	if (!try_others)
221		return (NULL);
222
223	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
224		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
225		return (NULL);
226	}
227	rpc_createerr.cf_stat = RPC_SUCCESS;
228	while (clnt == NULL) {
229		if ((nconf = __rpc_getconf(handle)) == NULL) {
230			if (rpc_createerr.cf_stat == RPC_SUCCESS)
231				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
232			break;
233		}
234		clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
235		if (clnt)
236			break;
237		else {
238			/*
239			 *	Since we didn't get a name-to-address
240			 *	translation failure here, we remember
241			 *	this particular error.  The object of
242			 *	this is to enable us to return to the
243			 *	caller a more-specific error than the
244			 *	unhelpful ``Name to address translation
245			 *	failed'' which might well occur if we
246			 *	merely returned the last error (because
247			 *	the local loopbacks are typically the
248			 *	last ones in /etc/netconfig and the most
249			 *	likely to be unable to translate a host
250			 *	name).  We also check for a more
251			 *	meaningful error than ``unknown host
252			 *	name'' for the same reasons.
253			 */
254			if (rpc_createerr.cf_stat == RPC_SYSTEMERROR) {
255				syslog(LOG_ERR, "clnt_create_timed: "
256					"RPC_SYSTEMERROR.");
257				break;
258			}
259
260			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
261			    rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
262				save_cf_stat = rpc_createerr.cf_stat;
263				save_cf_error = rpc_createerr.cf_error;
264			}
265		}
266	}
267
268	/*
269	 *	Attempt to return an error more specific than ``Name to address
270	 *	translation failed'' or ``unknown host name''
271	 */
272	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
273				rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
274					(save_cf_stat != RPC_SUCCESS)) {
275		rpc_createerr.cf_stat = save_cf_stat;
276		rpc_createerr.cf_error = save_cf_error;
277	}
278	__rpc_endconf(handle);
279	return (clnt);
280}
281
282/*
283 * Create a client handle for a well known service or a specific port on
284 * host. This routine bypasses rpcbind and can be use to construct a client
285 * handle to services that are not registered with rpcbind or where the remote
286 * rpcbind is not available, e.g., the remote rpcbind port is blocked by a
287 * firewall. We construct a client handle and then ping the service's NULL
288 * proc to see that the service is really available. If the caller supplies
289 * a non zero port number, the service name is ignored and the port will be
290 * used. A non-zero port number limits the protocol family to inet or inet6.
291 */
292
293CLIENT *
294clnt_create_service_timed(const char *host, const char *service,
295			const rpcprog_t prog, const rpcvers_t vers,
296			const ushort_t port, const char *netclass,
297			const struct timeval *tmout)
298{
299	int fd;
300	void *handle;
301	CLIENT *clnt = NULL;
302	struct netconfig *nconf;
303	struct nd_hostserv hs;
304	struct nd_addrlist *raddrs;
305	struct t_bind *tbind = NULL;
306	struct timeval to;
307	char nettype_array[NETIDLEN];
308	char *nettype = &nettype_array[0];
309	char *hostname, *serv;
310	bool_t try_others;
311	extern int __rpc_minfd;
312
313
314	/*
315	 * handle const of netclass
316	 */
317	if (netclass == NULL)
318		nettype = NULL;
319	else {
320		size_t len = strlen(netclass);
321		if (len >= sizeof (nettype_array)) {
322			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
323			return (NULL);
324		}
325		(void) strcpy(nettype, netclass);
326	}
327
328	if (tmout == NULL) {
329		to.tv_sec = 10;
330		to.tv_usec = 0;
331	} else
332		to = *tmout;
333
334	if ((handle = __rpc_setconf(nettype)) == NULL) {
335		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
336		return (NULL);
337	}
338
339	/*
340	 * Sinct host, and service are const
341	 */
342	if (host == NULL || (hostname = strdup(host)) == NULL) {
343		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
344		rpc_createerr.cf_error.re_errno = (host ? errno : EINVAL);
345		rpc_createerr.cf_error.re_terrno = 0;
346		return (NULL);
347	}
348
349	if (service == NULL)
350		serv = NULL;
351	else if ((serv = strdup(service ? service : "")) == NULL) {
352		free(hostname);
353		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
354		rpc_createerr.cf_error.re_errno = errno;
355		rpc_createerr.cf_error.re_terrno = 0;
356		return (NULL);
357	}
358
359	hs.h_host = hostname;
360	hs.h_serv = port ? NULL : serv;
361
362	/*
363	 * Check to see if a rendezvous over doors should be attempted.
364	 */
365	if (__rpc_try_doors(nettype, &try_others)) {
366		/*
367		 * Make sure this is the local host.
368		 */
369		if (__rpc_is_local_host(hostname)) {
370			if ((clnt = clnt_door_create(prog, vers, 0)) != NULL)
371				goto done;
372			else if (rpc_createerr.cf_stat == RPC_SYSTEMERROR)
373				goto done;
374		} else {
375			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
376		}
377	}
378	if (!try_others)
379		goto done;
380
381	rpc_createerr.cf_stat = RPC_SUCCESS;
382	while (clnt == NULL) {
383		tbind = NULL;
384		if ((nconf = __rpc_getconf(handle)) == NULL) {
385			if (rpc_createerr.cf_stat == RPC_SUCCESS)
386				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
387			break;
388		}
389
390		if (port) {
391			if (strcmp(nconf->nc_protofmly, NC_INET) != 0 &&
392			    strcmp(nconf->nc_protofmly, NC_INET6) != 0)
393				continue;
394		}
395
396		if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) < 0) {
397			rpc_createerr.cf_stat = RPC_TLIERROR;
398			rpc_createerr.cf_error.re_errno = errno;
399			rpc_createerr.cf_error.re_terrno = t_errno;
400			continue;
401		}
402
403		if (fd < __rpc_minfd)
404			fd = __rpc_raise_fd(fd);
405
406		/* LINTED pointer cast */
407		if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
408		    == NULL) {
409			(void) t_close(fd);
410			rpc_createerr.cf_stat = RPC_TLIERROR;
411			rpc_createerr.cf_error.re_errno = errno;
412			rpc_createerr.cf_error.re_terrno = t_errno;
413			continue;
414		}
415
416		if (netdir_getbyname(nconf, &hs, &raddrs) != ND_OK) {
417			if (rpc_createerr.cf_stat == RPC_SUCCESS)
418				rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
419			if (tbind)
420				(void) t_free((char *)tbind, T_BIND);
421			(void) t_close(fd);
422			continue;
423		}
424		(void) memcpy(tbind->addr.buf, raddrs->n_addrs->buf,
425		    raddrs->n_addrs->len);
426		tbind->addr.len = raddrs->n_addrs->len;
427		netdir_free((void *)raddrs, ND_ADDRLIST);
428
429		if (port) {
430			if (strcmp(nconf->nc_protofmly, NC_INET) == NULL)
431				/* LINTED pointer alignment */
432				((struct sockaddr_in *)
433				tbind->addr.buf)->sin_port = htons(port);
434			else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL)
435				/* LINTED pointer alignment */
436				((struct sockaddr_in6 *)
437				tbind->addr.buf)->sin6_port = htons(port);
438		}
439
440		clnt = _clnt_tli_create_timed(fd, nconf, &tbind->addr,
441					    prog, vers, 0, 0, &to);
442
443		if (clnt == NULL) {
444			if (tbind)
445				(void) t_free((char *)tbind, T_BIND);
446			(void) t_close(fd);
447			continue;
448		}
449
450		(void) CLNT_CONTROL(clnt, CLSET_FD_CLOSE, NULL);
451
452		/*
453		 * Check if we can reach the server with this clnt handle
454		 * Other clnt_create calls do a ping by contacting the
455		 * remote rpcbind, here will just try to execute the service's
456		 * NULL proc.
457		 */
458
459		rpc_createerr.cf_stat = clnt_call(clnt, NULLPROC,
460						xdr_void, 0, xdr_void, 0, to);
461
462		rpc_createerr.cf_error.re_errno = rpc_callerr.re_status;
463		rpc_createerr.cf_error.re_terrno = 0;
464
465		if (rpc_createerr.cf_stat != RPC_SUCCESS) {
466			clnt_destroy(clnt);
467			clnt = NULL;
468			if (tbind)
469				(void) t_free((char *)tbind, T_BIND);
470			continue;
471		} else
472			break;
473	}
474
475	__rpc_endconf(handle);
476	if (tbind)
477		(void) t_free((char *)tbind, T_BIND);
478
479done:
480	if (hostname)
481		free(hostname);
482	if (serv)
483		free(serv);
484
485	return (clnt);
486}
487
488/*
489 * Generic client creation: takes (servers name, program-number, netconf) and
490 * returns client handle. Default options are set, which the user can
491 * change using the rpc equivalent of ioctl()'s : clnt_control()
492 * It finds out the server address from rpcbind and calls clnt_tli_create().
493 *
494 * It calls clnt_tp_create_timed() with the default timeout.
495 */
496CLIENT *
497clnt_tp_create(const char *hostname, const rpcprog_t prog, const rpcvers_t vers,
498    const struct netconfig *nconf)
499{
500	return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
501}
502
503/*
504 * This has the same definition as clnt_tp_create(), except it
505 * takes an additional parameter - a pointer to a timeval structure.
506 * A NULL value for the timeout pointer indicates that the default
507 * value for the timeout should be used.
508 */
509CLIENT *
510clnt_tp_create_timed(const char *hostname, const rpcprog_t prog,
511    const rpcvers_t vers, const struct netconfig *nconf,
512    const struct timeval *tp)
513{
514	struct netbuf *svcaddr;			/* servers address */
515	CLIENT *cl = NULL;			/* client handle */
516	extern struct netbuf *__rpcb_findaddr_timed(rpcprog_t, rpcvers_t,
517	    struct netconfig *, char *, CLIENT **, struct timeval *);
518
519	if (nconf == NULL) {
520		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
521		return (NULL);
522	}
523
524	/*
525	 * Get the address of the server
526	 */
527	if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
528			(struct netconfig *)nconf, (char *)hostname,
529			&cl, (struct timeval *)tp)) == NULL) {
530		/* appropriate error number is set by rpcbind libraries */
531		return (NULL);
532	}
533	if (cl == NULL) {
534		cl = _clnt_tli_create_timed(RPC_ANYFD, nconf, svcaddr,
535					prog, vers, 0, 0, tp);
536	} else {
537		/* Reuse the CLIENT handle and change the appropriate fields */
538		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
539			if (cl->cl_netid == NULL) {
540				cl->cl_netid = strdup(nconf->nc_netid);
541				if (cl->cl_netid == NULL) {
542					netdir_free((char *)svcaddr, ND_ADDR);
543					rpc_createerr.cf_stat = RPC_SYSTEMERROR;
544					syslog(LOG_ERR,
545						"clnt_tp_create_timed: "
546						"strdup failed.");
547					return (NULL);
548				}
549			}
550			if (cl->cl_tp == NULL) {
551				cl->cl_tp = strdup(nconf->nc_device);
552				if (cl->cl_tp == NULL) {
553					netdir_free((char *)svcaddr, ND_ADDR);
554					if (cl->cl_netid)
555						free(cl->cl_netid);
556					rpc_createerr.cf_stat = RPC_SYSTEMERROR;
557					syslog(LOG_ERR,
558						"clnt_tp_create_timed: "
559						"strdup failed.");
560					return (NULL);
561				}
562			}
563			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
564			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
565		} else {
566			CLNT_DESTROY(cl);
567			cl = _clnt_tli_create_timed(RPC_ANYFD, nconf, svcaddr,
568					prog, vers, 0, 0, tp);
569		}
570	}
571	netdir_free((char *)svcaddr, ND_ADDR);
572	return (cl);
573}
574
575/*
576 * Generic client creation:  returns client handle.
577 * Default options are set, which the user can
578 * change using the rpc equivalent of ioctl()'s : clnt_control().
579 * If fd is RPC_ANYFD, it will be opened using nconf.
580 * It will be bound if not so.
581 * If sizes are 0; appropriate defaults will be chosen.
582 */
583CLIENT *
584clnt_tli_create(const int fd, const struct netconfig *nconf,
585    struct netbuf *svcaddr, const rpcprog_t prog, const rpcvers_t vers,
586    const uint_t sendsz, const uint_t recvsz)
587{
588	return (_clnt_tli_create_timed(fd, nconf, svcaddr, prog, vers, sendsz,
589		recvsz, NULL));
590}
591
592/*
593 * This has the same definition as clnt_tli_create(), except it
594 * takes an additional parameter - a pointer to a timeval structure.
595 *
596 * Not a public interface. This is for clnt_create_timed,
597 * clnt_create_vers_times, clnt_tp_create_timed to pass down  the
598 * timeout value to COTS creation routine.
599 * (for bug 4049792: clnt_create_timed does not time out)
600 */
601CLIENT *
602_clnt_tli_create_timed(int fd, const struct netconfig *nconf,
603	struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers, uint_t sendsz,
604	uint_t recvsz, const struct timeval *tp)
605{
606	CLIENT *cl;			/* client handle */
607	struct t_info tinfo;		/* transport info */
608	bool_t madefd;			/* whether fd opened here */
609	t_scalar_t servtype;
610	int retval;
611	extern int __rpc_minfd;
612
613	if (fd == RPC_ANYFD) {
614		if (nconf == NULL) {
615			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
616			return (NULL);
617		}
618
619		fd = t_open(nconf->nc_device, O_RDWR, NULL);
620		if (fd == -1)
621			goto err;
622		if (fd < __rpc_minfd)
623			fd = __rpc_raise_fd(fd);
624		madefd = TRUE;
625		if (t_bind(fd, NULL, NULL) == -1)
626			goto err;
627		switch (nconf->nc_semantics) {
628		case NC_TPI_CLTS:
629			servtype = T_CLTS;
630			break;
631		case NC_TPI_COTS:
632			servtype = T_COTS;
633			break;
634		case NC_TPI_COTS_ORD:
635			servtype = T_COTS_ORD;
636			break;
637		default:
638			if (t_getinfo(fd, &tinfo) == -1)
639				goto err;
640			servtype = tinfo.servtype;
641			break;
642		}
643	} else {
644		int state;		/* Current state of provider */
645
646		/*
647		 * Sync the opened fd.
648		 * Check whether bound or not, else bind it
649		 */
650		if (((state = t_sync(fd)) == -1) ||
651		    ((state == T_UNBND) && (t_bind(fd, NULL, NULL) == -1)) ||
652		    (t_getinfo(fd, &tinfo) == -1))
653			goto err;
654		servtype = tinfo.servtype;
655		madefd = FALSE;
656	}
657
658	switch (servtype) {
659	case T_COTS:
660		cl = _clnt_vc_create_timed(fd, svcaddr, prog, vers, sendsz,
661				recvsz, tp);
662		break;
663	case T_COTS_ORD:
664		if (nconf && ((strcmp(nconf->nc_protofmly, NC_INET) == 0) ||
665		    (strcmp(nconf->nc_protofmly, NC_INET6) == 0))) {
666			retval =  __td_setnodelay(fd);
667			if (retval == -1)
668				goto err;
669		}
670		cl = _clnt_vc_create_timed(fd, svcaddr, prog, vers, sendsz,
671			recvsz, tp);
672		break;
673	case T_CLTS:
674		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
675		break;
676	default:
677		goto err;
678	}
679
680	if (cl == NULL)
681		goto err1; /* borrow errors from clnt_dg/vc creates */
682	if (nconf) {
683		cl->cl_netid = strdup(nconf->nc_netid);
684		if (cl->cl_netid == NULL) {
685			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
686			rpc_createerr.cf_error.re_errno = errno;
687			rpc_createerr.cf_error.re_terrno = 0;
688			syslog(LOG_ERR,
689				"clnt_tli_create: strdup failed");
690			goto err1;
691		}
692		cl->cl_tp = strdup(nconf->nc_device);
693		if (cl->cl_tp == NULL) {
694			if (cl->cl_netid)
695				free(cl->cl_netid);
696			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
697			rpc_createerr.cf_error.re_errno = errno;
698			rpc_createerr.cf_error.re_terrno = 0;
699			syslog(LOG_ERR,
700				"clnt_tli_create: strdup failed");
701			goto err1;
702		}
703	} else {
704		struct netconfig *nc;
705
706		if ((nc = __rpcfd_to_nconf(fd, servtype)) != NULL) {
707			if (nc->nc_netid) {
708				cl->cl_netid = strdup(nc->nc_netid);
709				if (cl->cl_netid == NULL) {
710					rpc_createerr.cf_stat = RPC_SYSTEMERROR;
711					rpc_createerr.cf_error.re_errno = errno;
712					rpc_createerr.cf_error.re_terrno = 0;
713					syslog(LOG_ERR,
714						"clnt_tli_create: "
715						"strdup failed");
716					goto err1;
717				}
718			}
719			if (nc->nc_device) {
720				cl->cl_tp = strdup(nc->nc_device);
721				if (cl->cl_tp == NULL) {
722					if (cl->cl_netid)
723						free(cl->cl_netid);
724					rpc_createerr.cf_stat = RPC_SYSTEMERROR;
725					rpc_createerr.cf_error.re_errno = errno;
726					rpc_createerr.cf_error.re_terrno = 0;
727					syslog(LOG_ERR,
728						"clnt_tli_create: "
729						"strdup failed");
730					goto err1;
731				}
732			}
733			freenetconfigent(nc);
734		}
735		if (cl->cl_netid == NULL)
736			cl->cl_netid = "";
737		if (cl->cl_tp == NULL)
738			cl->cl_tp = "";
739	}
740	if (madefd) {
741		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
742/*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
743	};
744
745	return (cl);
746
747err:
748	rpc_createerr.cf_stat = RPC_TLIERROR;
749	rpc_createerr.cf_error.re_errno = errno;
750	rpc_createerr.cf_error.re_terrno = t_errno;
751err1:	if (madefd)
752		(void) t_close(fd);
753	return (NULL);
754}
755
756/*
757 *  To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
758 *  we try to not use them.  The __rpc_raise_fd() routine will dup
759 *  a descriptor to a higher value.  If we fail to do it, we continue
760 *  to use the old one (and hope for the best).
761 */
762int __rpc_minfd = 3;
763
764int
765__rpc_raise_fd(int fd)
766{
767	int nfd;
768
769	if (fd >= __rpc_minfd)
770		return (fd);
771
772	if ((nfd = fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
773		return (fd);
774
775	if (t_sync(nfd) == -1) {
776		(void) close(nfd);
777		return (fd);
778	}
779
780	if (t_close(fd) == -1) {
781		/* this is okay, we will syslog an error, then use the new fd */
782		(void) syslog(LOG_ERR,
783			"could not t_close() fd %d; mem & fd leak", fd);
784	}
785
786	return (nfd);
787}
788