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 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 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 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #ifdef PORTMAP 38 /* 39 * rpc_soc.c 40 * 41 * The backward compatibility routines for the earlier implementation 42 * of RPC, where the only transports supported were tcp/ip and udp/ip. 43 * Based on berkeley socket abstraction, now implemented on the top 44 * of TLI/Streams 45 */ 46 47 #include "mt.h" 48 #include "rpc_mt.h" 49 #include <stdio.h> 50 #include <sys/types.h> 51 #include <rpc/rpc.h> 52 #include <netinet/in.h> 53 #include <sys/socket.h> 54 #include <netdb.h> 55 #include <netdir.h> 56 #include <errno.h> 57 #include <sys/syslog.h> 58 #include <rpc/pmap_clnt.h> 59 #include <rpc/pmap_prot.h> 60 #include <rpc/nettype.h> 61 #include <syslog.h> 62 #include <string.h> 63 #include <stdlib.h> 64 #include <unistd.h> 65 66 int __rpc_bindresvport(int, struct sockaddr_in *, int *, int); 67 int __rpc_bindresvport_ipv6(int, struct sockaddr *, int *, int, char *); 68 void get_myaddress_ipv6(char *, struct sockaddr *); 69 70 extern mutex_t rpcsoc_lock; 71 72 /* 73 * A common clnt create routine 74 */ 75 static CLIENT * 76 clnt_com_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, 77 int *sockp, uint_t sendsz, uint_t recvsz, char *tp) 78 { 79 CLIENT *cl; 80 int madefd = FALSE; 81 int fd = *sockp; 82 struct t_info tinfo; 83 struct netconfig *nconf; 84 int port; 85 struct netbuf bindaddr; 86 extern int __rpc_minfd; 87 bool_t locked = TRUE; 88 89 (void) mutex_lock(&rpcsoc_lock); 90 if ((nconf = __rpc_getconfip(tp)) == NULL) { 91 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 92 (void) mutex_unlock(&rpcsoc_lock); 93 return (NULL); 94 } 95 if (fd == RPC_ANYSOCK) { 96 fd = t_open(nconf->nc_device, O_RDWR, &tinfo); 97 if (fd == -1) 98 goto syserror; 99 if (fd < __rpc_minfd) 100 fd = __rpc_raise_fd(fd); 101 madefd = TRUE; 102 } else { 103 if (t_getinfo(fd, &tinfo) == -1) 104 goto syserror; 105 } 106 107 if (raddr->sin_port == 0) { 108 uint_t proto; 109 ushort_t sport; 110 111 /* pmap_getport is recursive */ 112 (void) mutex_unlock(&rpcsoc_lock); 113 proto = strcmp(tp, "udp") == 0 ? IPPROTO_UDP : IPPROTO_TCP; 114 sport = pmap_getport(raddr, prog, vers, proto); 115 if (sport == 0) { 116 locked = FALSE; 117 goto err; 118 } 119 raddr->sin_port = htons(sport); 120 /* pmap_getport is recursive */ 121 (void) mutex_lock(&rpcsoc_lock); 122 } 123 124 /* Transform sockaddr_in to netbuf */ 125 bindaddr.maxlen = bindaddr.len = __rpc_get_a_size(tinfo.addr); 126 bindaddr.buf = (char *)raddr; 127 128 (void) __rpc_bindresvport(fd, NULL, &port, 0); 129 cl = clnt_tli_create(fd, nconf, &bindaddr, prog, vers, 130 sendsz, recvsz); 131 if (cl) { 132 if (madefd == TRUE) { 133 /* 134 * The fd should be closed while destroying the handle. 135 */ 136 (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL); 137 *sockp = fd; 138 } 139 (void) freenetconfigent(nconf); 140 (void) mutex_unlock(&rpcsoc_lock); 141 return (cl); 142 } 143 goto err; 144 145 syserror: 146 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 147 rpc_createerr.cf_error.re_errno = errno; 148 rpc_createerr.cf_error.re_terrno = t_errno; 149 150 err: if (madefd == TRUE) 151 (void) t_close(fd); 152 (void) freenetconfigent(nconf); 153 if (locked == TRUE) 154 (void) mutex_unlock(&rpcsoc_lock); 155 return (NULL); 156 } 157 158 CLIENT * 159 clntudp_bufcreate(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, 160 struct timeval wait, int *sockp, uint_t sendsz, uint_t recvsz) 161 { 162 CLIENT *cl; 163 164 cl = clnt_com_create(raddr, prog, vers, sockp, sendsz, recvsz, "udp"); 165 if (cl == NULL) 166 return (NULL); 167 (void) CLNT_CONTROL(cl, CLSET_RETRY_TIMEOUT, (char *)&wait); 168 return (cl); 169 } 170 171 CLIENT * 172 clntudp_create(struct sockaddr_in *raddr, rpcprog_t program, rpcvers_t version, 173 struct timeval wait, int *sockp) 174 { 175 return (clntudp_bufcreate(raddr, program, version, wait, sockp, 176 UDPMSGSIZE, UDPMSGSIZE)); 177 } 178 179 CLIENT * 180 clnttcp_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers, 181 int *sockp, uint_t sendsz, uint_t recvsz) 182 { 183 return (clnt_com_create(raddr, prog, vers, sockp, sendsz, 184 recvsz, "tcp")); 185 } 186 187 CLIENT * 188 clntraw_create(rpcprog_t prog, rpcvers_t vers) 189 { 190 return (clnt_raw_create(prog, vers)); 191 } 192 193 /* 194 * A common server create routine 195 */ 196 static SVCXPRT * 197 svc_com_create(int fd, uint_t sendsize, uint_t recvsize, char *netid) 198 { 199 struct netconfig *nconf; 200 SVCXPRT *svc; 201 int madefd = FALSE; 202 int port; 203 int res; 204 205 if ((nconf = __rpc_getconfip(netid)) == NULL) { 206 (void) syslog(LOG_ERR, "Could not get %s transport", netid); 207 return (NULL); 208 } 209 if (fd == RPC_ANYSOCK) { 210 fd = t_open(nconf->nc_device, O_RDWR, NULL); 211 if (fd == -1) { 212 char errorstr[100]; 213 214 __tli_sys_strerror(errorstr, sizeof (errorstr), 215 t_errno, errno); 216 (void) syslog(LOG_ERR, 217 "svc%s_create: could not open connection : %s", netid, 218 errorstr); 219 (void) freenetconfigent(nconf); 220 return (NULL); 221 } 222 madefd = TRUE; 223 } 224 225 res = __rpc_bindresvport(fd, NULL, &port, 8); 226 svc = svc_tli_create(fd, nconf, NULL, 227 sendsize, recvsize); 228 (void) freenetconfigent(nconf); 229 if (svc == NULL) { 230 if (madefd) 231 (void) t_close(fd); 232 return (NULL); 233 } 234 if (res == -1) 235 /* LINTED pointer cast */ 236 port = (((struct sockaddr_in *)svc->xp_ltaddr.buf)->sin_port); 237 svc->xp_port = ntohs(port); 238 return (svc); 239 } 240 241 SVCXPRT * 242 svctcp_create(int fd, uint_t sendsize, uint_t recvsize) 243 { 244 return (svc_com_create(fd, sendsize, recvsize, "tcp")); 245 } 246 247 SVCXPRT * 248 svcudp_bufcreate(int fd, uint_t sendsz, uint_t recvsz) 249 { 250 return (svc_com_create(fd, sendsz, recvsz, "udp")); 251 } 252 253 SVCXPRT * 254 svcfd_create(int fd, uint_t sendsize, uint_t recvsize) 255 { 256 return (svc_fd_create(fd, sendsize, recvsize)); 257 } 258 259 260 SVCXPRT * 261 svcudp_create(int fd) 262 { 263 return (svc_com_create(fd, UDPMSGSIZE, UDPMSGSIZE, "udp")); 264 } 265 266 SVCXPRT * 267 svcraw_create(void) 268 { 269 return (svc_raw_create()); 270 } 271 272 /* 273 * Bind a fd to a privileged IP port. 274 * This is slightly different from the code in netdir_options 275 * because it has a different interface - main thing is that it 276 * needs to know its own address. We also wanted to set the qlen. 277 * t_getname() can be used for those purposes and perhaps job can be done. 278 */ 279 int 280 __rpc_bindresvport_ipv6(int fd, struct sockaddr *sin, int *portp, int qlen, 281 char *fmly) 282 { 283 int res; 284 static in_port_t port, *sinport; 285 struct sockaddr_in6 myaddr; 286 int i; 287 struct t_bind tbindstr, *tres; 288 struct t_info tinfo; 289 extern mutex_t portnum_lock; 290 291 /* VARIABLES PROTECTED BY portnum_lock: port */ 292 293 #define STARTPORT 600 294 #define ENDPORT (IPPORT_RESERVED - 1) 295 #define NPORTS (ENDPORT - STARTPORT + 1) 296 297 if (sin == 0 && fmly == 0) { 298 errno = EINVAL; 299 return (-1); 300 } 301 if (geteuid()) { 302 errno = EACCES; 303 return (-1); 304 } 305 if ((i = t_getstate(fd)) != T_UNBND) { 306 if (t_errno == TBADF) 307 errno = EBADF; 308 if (i != -1) 309 errno = EISCONN; 310 return (-1); 311 } 312 if (sin == 0) { 313 sin = (struct sockaddr *)&myaddr; 314 get_myaddress_ipv6(fmly, sin); 315 } 316 if (sin->sa_family == AF_INET) { 317 /* LINTED pointer cast */ 318 sinport = &((struct sockaddr_in *)sin)->sin_port; 319 } else if (sin->sa_family == AF_INET6) { 320 /* LINTED pointer cast */ 321 sinport = &((struct sockaddr_in6 *)sin)->sin6_port; 322 } else { 323 errno = EPFNOSUPPORT; 324 return (-1); 325 } 326 327 /* Transform sockaddr to netbuf */ 328 if (t_getinfo(fd, &tinfo) == -1) { 329 return (-1); 330 } 331 /* LINTED pointer cast */ 332 tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR); 333 if (tres == NULL) 334 return (-1); 335 336 tbindstr.qlen = qlen; 337 tbindstr.addr.buf = (char *)sin; 338 tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr); 339 /* LINTED pointer cast */ 340 sin = (struct sockaddr *)tbindstr.addr.buf; 341 342 res = -1; 343 (void) mutex_lock(&portnum_lock); 344 if (port == 0) 345 port = (getpid() % NPORTS) + STARTPORT; 346 for (i = 0; i < NPORTS; i++) { 347 *sinport = htons(port++); 348 if (port > ENDPORT) 349 port = STARTPORT; 350 res = t_bind(fd, &tbindstr, tres); 351 if (res == 0) { 352 if ((tbindstr.addr.len == tres->addr.len) && 353 (memcmp(tbindstr.addr.buf, tres->addr.buf, 354 (int)tres->addr.len) == 0)) 355 break; 356 (void) t_unbind(fd); 357 res = -1; 358 } else if (t_errno != TSYSERR || errno != EADDRINUSE) 359 break; 360 } 361 (void) mutex_unlock(&portnum_lock); 362 363 if ((portp != NULL) && (res == 0)) 364 *portp = *sinport; 365 (void) t_free((char *)tres, T_BIND); 366 return (res); 367 } 368 369 int 370 __rpc_bindresvport(int fd, struct sockaddr_in *sin, int *portp, int qlen) 371 { 372 return (__rpc_bindresvport_ipv6(fd, (struct sockaddr *)sin, portp, 373 qlen, NC_INET)); 374 } 375 376 /* 377 * Get clients IP address. 378 * don't use gethostbyname, which would invoke yellow pages 379 * Remains only for backward compatibility reasons. 380 * Used mainly by the portmapper so that it can register 381 * with itself. Also used by pmap*() routines 382 */ 383 void 384 get_myaddress_ipv6(char *fmly, struct sockaddr *addr) 385 { 386 if (fmly != 0 && strcmp(fmly, NC_INET6) == 0) { 387 /* LINTED pointer cast */ 388 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; 389 (void) memset(sin6, 0, sizeof (*sin6)); 390 sin6->sin6_family = AF_INET6; 391 sin6->sin6_port = htons(PMAPPORT); 392 if (__can_use_af(AF_INET6)) { 393 /* Local copy of in6addr_any to avoid -lsocket */ 394 struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; 395 sin6->sin6_addr = in6addr_any; 396 } else { 397 struct in_addr in4; 398 in4.s_addr = INADDR_ANY; 399 IN6_INADDR_TO_V4MAPPED(&in4, &sin6->sin6_addr); 400 } 401 } else { 402 /* LINTED pointer cast */ 403 struct sockaddr_in *sin = (struct sockaddr_in *)addr; 404 (void) memset(sin, 0, sizeof (*sin)); 405 sin->sin_family = AF_INET; 406 sin->sin_port = htons(PMAPPORT); 407 sin->sin_addr.s_addr = INADDR_ANY; 408 } 409 } 410 411 void 412 get_myaddress(struct sockaddr_in *addr) 413 { 414 get_myaddress_ipv6(0, (struct sockaddr *)addr); 415 } 416 417 /* 418 * Get port used by specified service on specified host. 419 * Exists for source compatibility only. 420 * Obsoleted by rpcb_getaddr(). 421 */ 422 ushort_t 423 getrpcport(char *host, rpcprog_t prognum, rpcvers_t versnum, 424 rpcprot_t proto) 425 { 426 struct sockaddr_in addr; 427 struct hostent *hp; 428 429 if ((hp = gethostbyname(host)) == NULL) 430 return (0); 431 (void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); 432 addr.sin_family = AF_INET; 433 addr.sin_port = 0; 434 return (pmap_getport(&addr, prognum, versnum, proto)); 435 } 436 437 /* 438 * For connectionless "udp" transport. Obsoleted by rpc_call(). 439 */ 440 int 441 callrpc(char *host, rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum, 442 xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) 443 { 444 return ((int)rpc_call(host, prognum, versnum, procnum, inproc, 445 in, outproc, out, "udp")); 446 } 447 448 /* 449 * For connectionless kind of transport. Obsoleted by rpc_reg() 450 */ 451 int 452 registerrpc(rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum, 453 char *(*progname)(), xdrproc_t inproc, xdrproc_t outproc) 454 { 455 return (rpc_reg(prognum, versnum, procnum, progname, inproc, 456 outproc, "udp")); 457 } 458 459 /* 460 * All the following clnt_broadcast stuff is convulated; it supports 461 * the earlier calling style of the callback function 462 */ 463 static pthread_key_t clnt_broadcast_key; 464 static resultproc_t clnt_broadcast_result_main; 465 466 /* 467 * Need to translate the netbuf address into sockaddr_in address. 468 * Dont care about netid here. 469 */ 470 /* ARGSUSED2 */ 471 static bool_t 472 rpc_wrap_bcast(char *resultp, struct netbuf *addr, struct netconfig *nconf) 473 { 474 resultproc_t clnt_broadcast_result; 475 476 clnt_broadcast_result = thr_main()? clnt_broadcast_result_main : 477 (resultproc_t)pthread_getspecific(clnt_broadcast_key); 478 return ((*clnt_broadcast_result)(resultp, 479 /* LINTED pointer cast */ 480 (struct sockaddr_in *)addr->buf)); 481 } 482 483 /* 484 * Broadcasts on UDP transport. Obsoleted by rpc_broadcast(). 485 */ 486 enum clnt_stat 487 clnt_broadcast(rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, xdrproc_t xargs, 488 caddr_t argsp, xdrproc_t xresults, 489 caddr_t resultsp, resultproc_t eachresult) 490 { 491 extern mutex_t tsd_lock; 492 493 if (thr_main()) { 494 clnt_broadcast_result_main = eachresult; 495 } else { 496 if (clnt_broadcast_key == 0) { 497 (void) mutex_lock(&tsd_lock); 498 if (clnt_broadcast_key == 0) 499 (void) pthread_key_create(&clnt_broadcast_key, 500 NULL); 501 (void) mutex_unlock(&tsd_lock); 502 } 503 (void) pthread_setspecific(clnt_broadcast_key, 504 (void *)eachresult); 505 } 506 return (rpc_broadcast(prog, vers, proc, xargs, argsp, xresults, 507 resultsp, (resultproc_t)rpc_wrap_bcast, "udp")); 508 } 509 510 /* 511 * Create the client des authentication object. Obsoleted by 512 * authdes_seccreate(). 513 */ 514 AUTH * 515 authdes_create(char *servername, uint_t window, struct sockaddr_in *syncaddr, 516 des_block *ckey) 517 { 518 char *hostname = NULL; 519 520 if (syncaddr) { 521 /* 522 * Change addr to hostname, because that is the way 523 * new interface takes it. 524 */ 525 struct netconfig *nconf; 526 struct netbuf nb_syncaddr; 527 struct nd_hostservlist *hlist; 528 AUTH *nauth; 529 int fd; 530 struct t_info tinfo; 531 532 if ((nconf = __rpc_getconfip("udp")) == NULL && 533 (nconf = __rpc_getconfip("tcp")) == NULL) 534 goto fallback; 535 536 /* Transform sockaddr_in to netbuf */ 537 if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1) { 538 (void) freenetconfigent(nconf); 539 goto fallback; 540 } 541 (void) t_close(fd); 542 nb_syncaddr.maxlen = nb_syncaddr.len = 543 __rpc_get_a_size(tinfo.addr); 544 nb_syncaddr.buf = (char *)syncaddr; 545 if (netdir_getbyaddr(nconf, &hlist, &nb_syncaddr)) { 546 (void) freenetconfigent(nconf); 547 goto fallback; 548 } 549 if (hlist && hlist->h_cnt > 0 && hlist->h_hostservs) 550 hostname = hlist->h_hostservs->h_host; 551 nauth = authdes_seccreate(servername, window, hostname, ckey); 552 (void) netdir_free((char *)hlist, ND_HOSTSERVLIST); 553 (void) freenetconfigent(nconf); 554 return (nauth); 555 } 556 fallback: 557 return (authdes_seccreate(servername, window, hostname, ckey)); 558 } 559 #endif /* PORTMAP */ 560