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 *
clnt_create_vers(const char * hostname,const rpcprog_t prog,rpcvers_t * vers_out,const rpcvers_t vers_low,const rpcvers_t vers_high,const char * nettype)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 *
clnt_create_vers_timed(const char * hostname,const rpcprog_t prog,rpcvers_t * vers_out,const rpcvers_t vers_low,const rpcvers_t vers_high,const char * nettype,const struct timeval * tp)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 *
clnt_create(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const char * nettype)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 *
clnt_create_timed(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const char * netclass,const struct timeval * tp)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 *
clnt_create_service_timed(const char * host,const char * service,const rpcprog_t prog,const rpcvers_t vers,const ushort_t port,const char * netclass,const struct timeval * tmout)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 *
clnt_tp_create(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const struct netconfig * nconf)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 *
clnt_tp_create_timed(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const struct netconfig * nconf,const struct timeval * tp)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 *
clnt_tli_create(const int fd,const struct netconfig * nconf,struct netbuf * svcaddr,const rpcprog_t prog,const rpcvers_t vers,const uint_t sendsz,const uint_t recvsz)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 *
_clnt_tli_create_timed(int fd,const struct netconfig * nconf,struct netbuf * svcaddr,rpcprog_t prog,rpcvers_t vers,uint_t sendsz,uint_t recvsz,const struct timeval * tp)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
__rpc_raise_fd(int fd)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