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