1 /* $NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $ */ 2 3 /* 4 * The contents of this file are subject to the Sun Standards 5 * License Version 1.0 the (the "License";) You may not use 6 * this file except in compliance with the License. You may 7 * obtain a copy of the License at lib/libc/rpc/LICENSE 8 * 9 * Software distributed under the License is distributed on 10 * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 11 * express or implied. See the License for the specific 12 * language governing rights and limitations under the License. 13 * 14 * The Original Code is Copyright 1998 by Sun Microsystems, Inc 15 * 16 * The Initial Developer of the Original Code is: Sun 17 * Microsystems, Inc. 18 * 19 * All Rights Reserved. 20 * 21 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 22 * unrestricted use provided that this legend is included on all tape 23 * media and as a part of the software program in whole or part. Users 24 * may copy or modify Sun RPC without charge, but are not authorized 25 * to license or distribute it to anyone else except as part of a product or 26 * program developed by the user. 27 * 28 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 29 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 31 * 32 * Sun RPC is provided with no support and without any obligation on the 33 * part of Sun Microsystems, Inc. to assist in its use, correction, 34 * modification or enhancement. 35 * 36 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 37 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 38 * OR ANY PART THEREOF. 39 * 40 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 41 * or profits or other special, indirect and consequential damages, even if 42 * Sun has been advised of the possibility of such damages. 43 * 44 * Sun Microsystems, Inc. 45 * 2550 Garcia Avenue 46 * Mountain View, California 94043 47 */ 48 49 /* #ident "@(#)clnt_generic.c 1.40 99/04/21 SMI" */ 50 51 #if defined(LIBC_SCCS) && !defined(lint) 52 static char *sccsid2 = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI"; 53 static char *sccsid = "from: @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC"; 54 #endif 55 #include <sys/cdefs.h> 56 __FBSDID("$FreeBSD$"); 57 58 /* 59 * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc. 60 * All rights reserved. 61 */ 62 #include "namespace.h" 63 #include "reentrant.h" 64 #include <sys/types.h> 65 #include <sys/fcntl.h> 66 #include <sys/socket.h> 67 #include <netinet/in.h> 68 #include <netinet/tcp.h> 69 #include <stdio.h> 70 #include <errno.h> 71 #include <netdb.h> 72 #include <syslog.h> 73 #include <rpc/rpc.h> 74 #include <rpc/nettype.h> 75 #include <string.h> 76 #include <stdlib.h> 77 #include <unistd.h> 78 #include "un-namespace.h" 79 #include "rpc_com.h" 80 81 extern bool_t __rpc_is_local_host(const char *); 82 int __rpc_raise_fd(int); 83 84 #ifndef NETIDLEN 85 #define NETIDLEN 32 86 #endif 87 88 89 /* 90 * Generic client creation with version checking the value of 91 * vers_out is set to the highest server supported value 92 * vers_low <= vers_out <= vers_high AND an error results 93 * if this can not be done. 94 * 95 * It calls clnt_create_vers_timed() with a NULL value for the timeout 96 * pointer, which indicates that the default timeout should be used. 97 */ 98 CLIENT * 99 clnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out, 100 rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype) 101 { 102 103 return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low, 104 vers_high, nettype, NULL)); 105 } 106 107 /* 108 * This the routine has the same definition as clnt_create_vers(), 109 * except it takes an additional timeout parameter - a pointer to 110 * a timeval structure. A NULL value for the pointer indicates 111 * that the default timeout value should be used. 112 */ 113 CLIENT * 114 clnt_create_vers_timed(const char *hostname, rpcprog_t prog, 115 rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high, 116 const char *nettype, const struct timeval *tp) 117 { 118 CLIENT *clnt; 119 struct timeval to; 120 enum clnt_stat rpc_stat; 121 struct rpc_err rpcerr; 122 123 clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp); 124 if (clnt == NULL) { 125 return (NULL); 126 } 127 to.tv_sec = 10; 128 to.tv_usec = 0; 129 rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, 130 (char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to); 131 if (rpc_stat == RPC_SUCCESS) { 132 *vers_out = vers_high; 133 return (clnt); 134 } 135 while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) { 136 unsigned int minvers, maxvers; 137 138 clnt_geterr(clnt, &rpcerr); 139 minvers = rpcerr.re_vers.low; 140 maxvers = rpcerr.re_vers.high; 141 if (maxvers < vers_high) 142 vers_high = maxvers; 143 else 144 vers_high--; 145 if (minvers > vers_low) 146 vers_low = minvers; 147 if (vers_low > vers_high) { 148 goto error; 149 } 150 CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high); 151 rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, 152 (char *)NULL, (xdrproc_t)xdr_void, 153 (char *)NULL, to); 154 if (rpc_stat == RPC_SUCCESS) { 155 *vers_out = vers_high; 156 return (clnt); 157 } 158 } 159 clnt_geterr(clnt, &rpcerr); 160 161 error: 162 rpc_createerr.cf_stat = rpc_stat; 163 rpc_createerr.cf_error = rpcerr; 164 clnt_destroy(clnt); 165 return (NULL); 166 } 167 168 /* 169 * Top level client creation routine. 170 * Generic client creation: takes (servers name, program-number, nettype) and 171 * returns client handle. Default options are set, which the user can 172 * change using the rpc equivalent of _ioctl()'s. 173 * 174 * It tries for all the netids in that particular class of netid until 175 * it succeeds. 176 * XXX The error message in the case of failure will be the one 177 * pertaining to the last create error. 178 * 179 * It calls clnt_create_timed() with the default timeout. 180 */ 181 CLIENT * 182 clnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers, 183 const char *nettype) 184 { 185 186 return (clnt_create_timed(hostname, prog, vers, nettype, NULL)); 187 } 188 189 /* 190 * This the routine has the same definition as clnt_create(), 191 * except it takes an additional timeout parameter - a pointer to 192 * a timeval structure. A NULL value for the pointer indicates 193 * that the default timeout value should be used. 194 * 195 * This function calls clnt_tp_create_timed(). 196 */ 197 CLIENT * 198 clnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers, 199 const char *netclass, const struct timeval *tp) 200 { 201 struct netconfig *nconf; 202 CLIENT *clnt = NULL; 203 void *handle; 204 enum clnt_stat save_cf_stat = RPC_SUCCESS; 205 struct rpc_err save_cf_error; 206 char nettype_array[NETIDLEN]; 207 char *nettype = &nettype_array[0]; 208 209 if (netclass == NULL) 210 nettype = NULL; 211 else { 212 size_t len = strlen(netclass); 213 if (len >= sizeof (nettype_array)) { 214 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 215 return (NULL); 216 } 217 strcpy(nettype, netclass); 218 } 219 220 if ((handle = __rpc_setconf((char *)nettype)) == NULL) { 221 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 222 return (NULL); 223 } 224 rpc_createerr.cf_stat = RPC_SUCCESS; 225 while (clnt == NULL) { 226 if ((nconf = __rpc_getconf(handle)) == NULL) { 227 if (rpc_createerr.cf_stat == RPC_SUCCESS) 228 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 229 break; 230 } 231 #ifdef CLNT_DEBUG 232 printf("trying netid %s\n", nconf->nc_netid); 233 #endif 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_N2AXLATEFAILURE && 255 rpc_createerr.cf_stat != RPC_UNKNOWNHOST) { 256 save_cf_stat = rpc_createerr.cf_stat; 257 save_cf_error = rpc_createerr.cf_error; 258 } 259 } 260 261 /* 262 * Attempt to return an error more specific than ``Name to address 263 * translation failed'' or ``unknown host name'' 264 */ 265 if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE || 266 rpc_createerr.cf_stat == RPC_UNKNOWNHOST) && 267 (save_cf_stat != RPC_SUCCESS)) { 268 rpc_createerr.cf_stat = save_cf_stat; 269 rpc_createerr.cf_error = save_cf_error; 270 } 271 __rpc_endconf(handle); 272 return (clnt); 273 } 274 275 /* 276 * Generic client creation: takes (servers name, program-number, netconf) and 277 * returns client handle. Default options are set, which the user can 278 * change using the rpc equivalent of _ioctl()'s : clnt_control() 279 * It finds out the server address from rpcbind and calls clnt_tli_create(). 280 * 281 * It calls clnt_tp_create_timed() with the default timeout. 282 */ 283 CLIENT * 284 clnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers, 285 const struct netconfig *nconf) 286 { 287 288 return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL)); 289 } 290 291 /* 292 * This has the same definition as clnt_tp_create(), except it 293 * takes an additional parameter - a pointer to a timeval structure. 294 * A NULL value for the timeout pointer indicates that the default 295 * value for the timeout should be used. 296 */ 297 CLIENT * 298 clnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers, 299 const struct netconfig *nconf, const struct timeval *tp) 300 { 301 struct netbuf *svcaddr; /* servers address */ 302 CLIENT *cl = NULL; /* client handle */ 303 304 if (nconf == NULL) { 305 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 306 return (NULL); 307 } 308 309 /* 310 * Get the address of the server 311 */ 312 if ((svcaddr = __rpcb_findaddr_timed(prog, vers, 313 (struct netconfig *)nconf, (char *)hostname, 314 &cl, (struct timeval *)tp)) == NULL) { 315 /* appropriate error number is set by rpcbind libraries */ 316 return (NULL); 317 } 318 if (cl == NULL) { 319 cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, 320 prog, vers, 0, 0); 321 } else { 322 /* Reuse the CLIENT handle and change the appropriate fields */ 323 if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) { 324 if (cl->cl_netid == NULL) 325 cl->cl_netid = strdup(nconf->nc_netid); 326 if (cl->cl_tp == NULL) 327 cl->cl_tp = strdup(nconf->nc_device); 328 (void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog); 329 (void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers); 330 } else { 331 CLNT_DESTROY(cl); 332 cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, 333 prog, vers, 0, 0); 334 } 335 } 336 free(svcaddr->buf); 337 free(svcaddr); 338 return (cl); 339 } 340 341 /* 342 * Generic client creation: returns client handle. 343 * Default options are set, which the user can 344 * change using the rpc equivalent of _ioctl()'s : clnt_control(). 345 * If fd is RPC_ANYFD, it will be opened using nconf. 346 * It will be bound if not so. 347 * If sizes are 0; appropriate defaults will be chosen. 348 */ 349 CLIENT * 350 clnt_tli_create(int fd, const struct netconfig *nconf, 351 struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers, 352 uint sendsz, uint recvsz) 353 { 354 CLIENT *cl; /* client handle */ 355 bool_t madefd = FALSE; /* whether fd opened here */ 356 long servtype; 357 int one = 1; 358 struct __rpc_sockinfo si; 359 extern int __rpc_minfd; 360 361 if (fd == RPC_ANYFD) { 362 if (nconf == NULL) { 363 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 364 return (NULL); 365 } 366 367 fd = __rpc_nconf2fd(nconf); 368 369 if (fd == -1) 370 goto err; 371 if (fd < __rpc_minfd) 372 fd = __rpc_raise_fd(fd); 373 madefd = TRUE; 374 servtype = nconf->nc_semantics; 375 if (!__rpc_fd2sockinfo(fd, &si)) 376 goto err; 377 bindresvport(fd, NULL); 378 } else { 379 if (!__rpc_fd2sockinfo(fd, &si)) 380 goto err; 381 servtype = __rpc_socktype2seman(si.si_socktype); 382 if (servtype == -1) { 383 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 384 return (NULL); 385 } 386 } 387 388 if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) { 389 rpc_createerr.cf_stat = RPC_UNKNOWNHOST; /* XXX */ 390 goto err1; 391 } 392 393 switch (servtype) { 394 case NC_TPI_COTS: 395 cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz); 396 break; 397 case NC_TPI_COTS_ORD: 398 if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) { 399 _setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, 400 sizeof (one)); 401 } 402 cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz); 403 break; 404 case NC_TPI_CLTS: 405 cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz); 406 break; 407 default: 408 goto err; 409 } 410 411 if (cl == NULL) 412 goto err1; /* borrow errors from clnt_dg/vc creates */ 413 if (nconf) { 414 cl->cl_netid = strdup(nconf->nc_netid); 415 cl->cl_tp = strdup(nconf->nc_device); 416 } else { 417 cl->cl_netid = ""; 418 cl->cl_tp = ""; 419 } 420 if (madefd) { 421 (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL); 422 /* (void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL); */ 423 }; 424 425 return (cl); 426 427 err: 428 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 429 rpc_createerr.cf_error.re_errno = errno; 430 err1: if (madefd) 431 (void)_close(fd); 432 return (NULL); 433 } 434 435 /* 436 * To avoid conflicts with the "magic" file descriptors (0, 1, and 2), 437 * we try to not use them. The __rpc_raise_fd() routine will dup 438 * a descriptor to a higher value. If we fail to do it, we continue 439 * to use the old one (and hope for the best). 440 */ 441 int __rpc_minfd = 3; 442 443 int 444 __rpc_raise_fd(int fd) 445 { 446 int nfd; 447 448 if (fd >= __rpc_minfd) 449 return (fd); 450 451 if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1) 452 return (fd); 453 454 if (_fsync(nfd) == -1) { 455 _close(nfd); 456 return (fd); 457 } 458 459 if (_close(fd) == -1) { 460 /* this is okay, we will syslog an error, then use the new fd */ 461 (void) syslog(LOG_ERR, 462 "could not close() fd %d; mem & fd leak", fd); 463 } 464 465 return (nfd); 466 } 467